Search code examples
javascriptreactjsreact-routerreact-context

React Router v6, guarded routes using Context API


I have the following setup:

App.js

import AppContext from './context/AppContext.js';
import router from './routes/routes.js';

function App() {
    const [token, setToken] = useState("");
    
    function login(token) {
        setToken(token);
    }

    return (
        <React.StrictMode>
            <AppContext.Provider value={{ token, login }}>
                <RouterProvider router={router} />
            </AppContext.Provider>
        </React.StrictMode>
    );
}

export default App;

routes.js

export default createBrowserRouter([
    {
        path: "/",
        element: <Root />,
        children: [
            {
                path: "home",
                element: <Home />
            },
            {
                path: "admin",
                element: <ProtectedRoute element={Admin} />
            },
            {
                path: "login",
                element: <Login />
            }
        ]
    }
]);

ProtectedRoute.js

export default function ProtectedRoute({ element: Component, ...rest }) {
    const ctx = useContext(AppContext);

    return (
        ctx.token !== "" ? <Component /> : <Navigate to="/login" />
    );
}

I want to be able check if the user is logged in by accessing information from a context, however the returned object always contains the default values. Is there a way to access context information with createBrowserRouter in this scenario?


Solution

  • The protected route component should be refactored to be rendered as a layout route component by rendering an Outlet instead of some passed prop.

    import { Navigate, Outlet } from 'react-router-dom';
    
    export default function ProtectedRoute() {
      const { token } = useContext(AppContext);
    
      return token
        ? <Outlet />
        : <Navigate to="/login" replace />;
    }
    
    export default createBrowserRouter([
      {
        path: "/",
        element: <Root />,
        children: [
          { path: "home", element: <Home /> },
          {
            element: <ProtectedRoute />,
            children: [
              {
                path: "admin",
                element: <Admin />
              },
              // ... other protected routes
            ],
          },
          { path: "login", element: <Login /> }
          // ... other unprotected routes
        ]
      }
    ]);