Which PrivateRouter realization is better: higher-order component or substitution?

Viewed 33

So recently I found out two ways of creating private routes in react.

  1. With a HOC (higher-order component):
const PrivateRoute = ({ user, children }) => {
  if (!user) {
    return <Navigate to="/home" replace />;
  }

  return children;
};


const App = () => {
  ...

  return (
    <>
      ...

      <Routes>
        <Route path="/home" element={<Home />} />
        <Route
          path="/privateroute"
          element={
            <PrivateRoute user={user}>
              <PrivateComponent />
            </PrivateRoute >
          }
        />
        ...
      </Routes>
    </>
  );
};
  1. With substituting routes completely
const App = () => {
...
  return (
    <>
      {user ? (
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/privateroute" element={<PrivateComponent />} />
          ...
        </Routes>
      ) : (
        <Routes>
          <Route path="/home" element={<Home />} />
          ...
        </Routes>
      )}
    </>
  );
}

My fellow colleague told me that the second way is quite bad since it completely erases some routes (if user is falsy then there is no route to /privateroute). But on my question why might that be bad he had no definitive answer. I couldn't find anything on the internet either. Any thoughts on which way is the best?

1 Answers

Between these two options, the first is the preferred solution since it keeps all routes mounted so they there will be no race condition between setting the user state and issuing an imperative navigation action to one of the protected routes. In other words, with the second implementation you have to wait for the user state to update and trigger a component rerender so the protected routes are mounted and available to be navigated to.

The second method also duplicates unauthenticated routes if it's all one or the other. Code duplication should be avoided.

Note however though that the first example isn't a Higher Order Component, it's just a wrapper component.

Note also that it's more common to create a PrivateRoute component as a Layout Route instead of as a Wrapper component. The change is trivial but it makes the component a little more wieldy. Render an Outlet component for nested routes instead of the children prop for a single wrapped child component.

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

const PrivateRoute = ({ user }) => {
  return user ? <Outlet /> : <Navigate to="/home" replace />;
};

Now instead of wrapping each individual route you want to protect you render a layout route that wraps an entire group of routes you want to protect. It makes your code more DRY.

const App = () => {
  ...

  return (
    <>
      ...

      <Routes>
        <Route path="/home" element={<Home />} />
        ... other unprotected routes ...

        <Route element={<PrivateRoute />}>
          <Route path="/privateroute" element={<PrivateComponent />} />
          ... other protected routes ...
        </Route>

        ... other unprotected routes ...
      </Routes>
    </>
  );
};
Related