Search code examples
reactjsreact-routerreact-router-domreact-context

Show respective page when user is logged in


My problem is that I display the respective page depending on whether a user is logged in or not. If the user is logged off, he should be forwarded to the login page and not be able to access any other pages. With the following code, nothing happens. If I change the user in the AuthProvider to a string value, however, I am forwarded to the MainPage, which means that it then works. Only if the user has the value null do I not get to the login page.

Matched leaf route at location "/" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.

 function App() {
 const { user } = useAuth();

 return (
  <Router>
  <Routes>
    <Route path="/">
      {user ? (
        <>
          <Route index element={<MainPage></MainPage>} />
        </>
      ) : (
        <>
          <Route path="login" element={<Login />} />
        </>
      )}
    </Route>
  </Routes>
</Router>
 );
}

authProvider.js

const AuthContext = createContext({});

export const AuthProvider = ({ children }) => {
return (
  <AuthContext.Provider value={{ user: null }}>
    {children}
  </AuthContext.Provider>
 );
};

export default function useAuth() {
  return useContext(AuthContext);
}

index.js

 const root = ReactDOM.createRoot(document.getElementById("root"));
 root.render(
 <AuthProvider>
   <App />
 </AuthProvider>
);

Solution

  • With the current implementation there isn't any "forwarding" happening. It appears you are just on route "/" and while user is falsey only the "/login" route is accessible. If you manually update the URL to "/login" you'll see the Login component correctly rendered as expected.

    Matched leaf route at location "/" does not have an element or Component. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.

    This warning is because the path is "/" and there is no leaf route, e.g. nested route, for it.

    <Routes>
      <Route path="/"> // <-- Not a leaf route, renders Outlet, this is ok
        {user ? (
          <>
            <Route index element={<MainPage />} /> // <-- "/"
          </>
        ) : (
          <>
            <Route path="login" element={<Login />} /> // <-- "/login"
            ??? <-- There is no "/" leaf route!
          </>
        )}
      </Route>
    </Routes>
    

    So far the there's no issue with the code, it functions exactly as intended. What I believe is the issue here is what you think the code should be doing. Like I said, the code here doesn't "forward", or redirect, to any route when the auth state changes. If the user is on "/" and logs out, there is nothing here to redirect the app to the "/login" route (other than manually navigating there via the address bar).

    The common pattern to handle route protection is to create layout routes that check the auth condition and render either an Outlet for the protected routes or a redirect to the login route. This also has the benefit of keeping all the routes mounted so there's no synchronicity issues between the auth state updating and the triggered rerender to mount the appropriate routes.

    Here is an example refactor:

    import {
      BrowserRouter as Router,
      Routes,
      Route,
      Navigate,
      Outlet
    } from "react-router-dom";
    
    const ProtectedRoutes = () => {
      const { user } = useAuth();
    
      if (user === undefined) {
        return null; // or loading indicator/spinner/etc
      }
    
      return user ? <Outlet /> : <Navigate to="/login" replace />;
    };
    
    function App() {
      return (
        <Router>
          <Routes>
            <Route element={<ProtectedRoutes />}>
              <Route path="/" element={<MainPage />} />
              {/* ... protected routes ... */}
            </Route>
    
            <Route path="login" element={<Login />} />
            {/* ... unprotected routes ... */}
          </Routes>
        </Router>
      );
    }
    

    Edit show-respective-page-when-user-is-logged-in