DEV Community

Eugene Tsarenko
Eugene Tsarenko

Posted on • Edited on

React Router Helpers – Taking routing in React to the next level 🚀

Image description

🤔 Imagine you are a Front-End React Engineer working at a custom software development company or for your mission impossible, haha, possible* project, and you need to implement routing in your React application. The first thing that would immediately pop up in your mind would be using an npm package named react-router-dom, which is pretty much the standard way of doing it, right?

Image description

However, do you sometimes feel there should be a much more efficient, secure, and simple way to implement route protection? Someway that improves how lazy components are loaded, so your app performance enhances? Or maybe there should be an easier way to debug the component loading lifecycle?

Image description

Well, what if I tell you an npm module named react-router-helper provides simple, efficient, and quick-to-set-up ways of doing all of the above and much more? You might be thinking that it’s just another npm package that involves different and complicated syntax and involves much refactoring, right? It’s definitely not like that, the coolest part is that its syntax is super simple and it doesn’t require much change of code or refactoring.

Cool features of react-router-helper package
⬇️ Some cool features of this package include but are not limited to:
✔️ Adds Route protection.
✔️ Providing data for component pages.
✔️ Lazy component support out of the box.
✔️ If the user doesn’t have access to a certain component, then that component won’t even be loaded over the network.
✔️ It makes debugging and inspection into a loading component pretty easy by providing detailed statuses with hooks on each step of component loading.

Simple setup

The setup of this package is pretty simple. All you need to do is:
👉 Install react-router-dom via your preferred package manager (npm/yarn).

For npm, npm i react-router-dom
For yarn, yarn add react-router-dom

👉 Install react-router-helpers via your preferred package manager (npm/yarn).

For npm, npm i @symfa/react-router-helpers
For yarn, yarn add @symfa/react-router-helpers

Migration was never this easy!
Imagine if you have an existing project codebase or the boilerplate template for adding a react-router-dom wrapped code layer and want to migrate to react-router-helper. The migration becomes super easy and happens without any major boilerplate code change except for swapping the tags with those used in react-router-helper if you’re using react-router-dom version 6 or above (Note: react-router-dom encourages its use with JavaScript objects ever since it’s version 6 was released because of much smoother and efficient experiences).

Image description

⬇️ Just follow the three steps mentioned below that demonstrate a code level before and after migration code, and you’re done with a complete migration:
🔯 Replace useRoutes tag with useRoutesWithHelper
Before migration (react-router-dom version)

import { useRoutes } from 'react-router-dom';

function App() {
  let element = useRoutes([
    {
      path: '/',
      element: <Dashboard />,
      children: [
        {
          path: 'messages',
          element: <DashboardMessages />,
        },
        { path: 'tasks', element: <DashboardTasks /> },
      ],
    },
    { path: 'team', element: <AboutPage /> },
  ]);
  return element;
}
Enter fullscreen mode Exit fullscreen mode

After migration (react-router-helper version)

import { useRoutesWithHelper } from '@symfa/react-router-helpers';

function App() {
  let element = useRoutesWithHelper([
    {
      path: '/',
      element: <Dashboard />,
      children: [
        {
          path: 'messages',
          element: <DashboardMessages />,
        },
        { path: 'tasks', element: <DashboardTasks /> },
      ],
    },
    { path: 'team', element: <AboutPage /> },
  ]);
  return element;
}
Enter fullscreen mode Exit fullscreen mode

🔯 Replace <Outlet/> with <HelperOutlet/>
Before migration (react-router-dom version)

import { Outlet } from 'react-router-dom';

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>

      <Outlet />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

After migration (react-router-helper version)

import { HelperOutlet } from '@symfa/react-router-helpers';

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>

      <HelperOutlet />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

🔯 Wrap your component into <RouteHelper /> if you are using Route and component style

Before migration (react-router-dom version)

root.render(
  <BrowserRouter>
    <Routes>
      <Route path='/' element={<Dashboard />} />
    </Routes>
  </BrowserRouter>
);
Enter fullscreen mode Exit fullscreen mode

After migration (react-router-helper version)

import { RouteHelper } from '@symfa/react-router-helpers';

root.render(
  <BrowserRouter>
    <Routes>
      <Route path='/' element={<RouteHelper element={<Dashboard />} />} />
    </Routes>
  </BrowserRouter>
);
Enter fullscreen mode Exit fullscreen mode

Simple to use Architecture

Do you often get panicked when you hear or learn about a complicated software technology architecture? Well, that’s NOT the case here because the entity structure of this amazing library is super simple.
👉 There are only 3 entities involved which are mentioned and described below:
✏️ Guard (Provides protection to a page that a certain unauthorized user or a regular user cannot access, for example, the admin page).
🔎 Let’s explore a very basic usage example workflow:

You need to create the guard function.

export const authorizationGuard = () => () => {
  return localStorage.getItem('token') !== null;
};
Enter fullscreen mode Exit fullscreen mode

You need to import this guard function in the main App component and add it to the element object where you’ve defined the route you want to protect from unauthorized access. Yes, it’s that simple!

import { useRoutesWithHelper } from '@symfa/react-router-helpers';

function App() {
  let element = useRoutesWithHelper([
    {
      path: '/',
      element: <Dashboard />,
      guards: [authorizationGuard],
    },
  ]);
  return element;
}
Enter fullscreen mode Exit fullscreen mode

✏️ Resolver (Helps while a component is loading, usually when an API call request is in the process).
As a Frontend React Engineer, we often interact with the backend via Rest API calls or GraphQL since we need to load data from the server side on our react components. Ideally, what we use useEffect to make the API request call and use useState to indicate the loading data state so that our end-user can view some action, i.e., fetching data, in this case, is happening during that short time interval. This is exactly where the Resolver comes into play.

🔎 Let’s explore a very basic usage example workflow:
The first step always remains the same. Create a resolver function.

export const userInfoResolver = () => async () => {
  const userInfo = await getUserInfo();

  return userInfo;
};
Enter fullscreen mode Exit fullscreen mode

For basic level usage, this step again remains the same. You just need to import the Resolver function into the main App component and add it to the element object where you want it to be applied.

import { useRoutesWithHelper } from '@symfa/react-router-helpers';

function App() {
  let element = useRoutesWithHelper([
    {
      path: '/',
      element: <Dashboard />,
      resolvers: {
        userInfo: userInfoResolver
      },
    },
  ]);
  return element;
}
Enter fullscreen mode Exit fullscreen mode

Let’s also explore a scenario with the Loading component.

import { useRoutesWithHelper } from '@symfa/react-router-helpers';

const Loading = () => {
  return <>Loading...</>;
};

function App() {
  let element = useRoutesWithHelper([
    {
      path: '/',
      element: <Dashboard />,
      loadingComponent: <Loading />,
      resolvers: {
        userInfo: userInfoResolver
      },
    },
  ]);
  return element;
}
Enter fullscreen mode Exit fullscreen mode

You must be wondering that showing the component loading is fine but how do you receive and use the data returned by the resolvers, right? Well, that’s super simple to do. All you need to do is extract out or destructure the object using the useResolver hook and use it in your component.

import { useResolver } from '@symfa/react-router-helpers';

export function Dashboard() {
  const { userInfo } = useResolver();

  return (<>
    <h2>Dashboard Page</h2>
    <h2>{userInfo.name}</h2>
  </>);
}
Enter fullscreen mode Exit fullscreen mode

✏️ Lazy component (Provides out of the box support for lazy components).
This is one of the most special features of this library because lazy component usage has never been this smooth and efficient.

👉 Following two features make it amazing:

  1. No need for any additional wrapper such as React.Suspense.
  2. Allows using guards to prevent the lazy component from loading. It means that if the user doesn’t have access to a lazy component page/component, that component won’t load.

🔎 Let’s explore a very basic usage example workflow:
Let’s first explore a typical usage of lazy component with react-router-dom.

import { useRoutes } from 'react-router-dom';
const Dashboard = React.lazy(() => import('./pages/Dashboard'));

function App() {
  let element = useRoutes([
    {
      path: '/',
      element: (
        <React.Suspense fallback={<>Loading</>}>
          <Dashboard />
        </React.Suspense>
      ),
    },
  ]);
  return element;
}
Enter fullscreen mode Exit fullscreen mode

Now, let’s see a react-router-helper version of it.

import { useRoutesWithHelper } from '@symfa/react-router-helpers';
const Dashboard = React.lazy(() => import('./pages/Dashboard'));

function App() {
  let element = useRoutesWithHelper([
    {
      path: '/',
      lazyElement: <Dashboard />,
    },
  ]);
  return element;
}
Enter fullscreen mode Exit fullscreen mode

You might be wondering about how to use it with a loading component showing a loading indicator. Well, we’ve got that also covered for you.

import { useRoutesWithHelper } from '@symfa/react-router-helpers';
const Dashboard = React.lazy(() => import('./pages/Dashboard'));

const LoadingComponent = () => <>Loading...</>;

function App() {
  let element = useRoutesWithHelper([
    {
      path: '/',
      lazyElement: <Dashboard />,
      loadingElement: <LoadingComponent />
    },
  ]);
  return element;
}
Enter fullscreen mode Exit fullscreen mode

Similarly, you might come across use-cases where you need to validate against certain uncertain events or errors. A very relevant and common example in this case could be that for some uncertain reason(s), your internet connection got interrupted.

❔ Now you might be thinking what to do in such a situation, right?
Well, just sit back and relax because our library got you covered with the help of useLazyStatus (used for providing detailed information about the lazy component status that can be received inside the LoadingComponent via the useLazyStatus hook) and useLazyError hook (used to provide detailed information about the error occurred while loading the lazy component).

🔎 Let’s explore a very basic usage example workflow:
Receiving lazy component statuses via the useLazyStatus hook.

import { useLazyStatus } from '@symfa/react-router-helpers';

export const LoadingComponent = () => {
  const lazyStatus = useLazyStatus();

  useEffect(() => {
    console.log(lazyStatus);
  }, [lazyStatus]);

  return <>Loading...</>;
};
Enter fullscreen mode Exit fullscreen mode

Now, let’s explore how to practically use the useLazyError hook to catch the lazy component error via the following code snippet.

import { useLazyStatus, useLazyError } from '@symfa/react-router-helpers';

export const LoadingComponent = () => {
  const lazyStatus = useLazyStatus();
  const lazyError = useLazyError();

  useEffect(() => {
    console.log(lazyStatus);
  }, [lazyStatus]);

  useEffect(() => {
    console.log(useLazyError);
  }, [useLazyError]);

  return <>Loading...</>;
};
Enter fullscreen mode Exit fullscreen mode

Looks pretty easy and straightforward, right? Well, that’s exactly how it is.

Note: You can explore further code snippets and further feature details about any of the entities from https://github.com/Aiscom-LLC/react-router-helpers#check-out-more-our-examples.

Status indicators making debugging look very simple

Imagine if you wanted to know the status of any of these main entities, or are stuck somewhere trying to debug the cause of what’s going wrong. Debugging, in this case, would probably take a lot of your time.

But here’s the cool thing about this library; it provides you with status messages for each entity so that you can easily get to know their status.

👉 There are four types of status mentioned below:
✔️ Initial – not initiated yet
✔️ Loading
✔️ Loaded
✔️ Failed – couldn’t work for some reason.

🔎 Now, let’s explore how we would get the statuses:
Nothing complicated here as well, the common functional syntax pattern is useEntityTypeStatus.

  1. To get guard statuses – useGuardStatus.
  2. To get resolver statuses – useResolverStatus.
  3. To get lazy component’s status – useLazyStatus.

In addition to these statuses, LoadingComponent is also used as a loading indicator.

Routing in react was never so easy, and leveraging the routing features to their fullest makes react-router-helpers a decent library to look into when implementing routing in your React application.

Happy routing!

Top comments (0)