I am using react-router v6 I am trying to protect some routes and not give access to users if they are not authenticated. I am using the token to determine if user is authenticated or not. After I login into the app using login details instead of returning todos route it doesn't show anything. I get this error message on the console Maximum depth update exceeded. However, if I'm not protecting the routes it works fine.
This is how I am protecting routes.
const token = useSelector(state => state.authDetails.token );
let routes = (
<Routes>
<Route path='/signUp' exact element={<SignUpForm />}/>
<Route path='/' exact element={<LoginForm />}/>
<Route path='*' replace element={<Navigate to="/" />}/>
</Routes>
)
if(token !== null ) {
routes = (
<Routes>
<Route path='/todos' element={<AddTodo/>}/>
<Route path='/logout' element={<Logout/>}/>
<Route path='*' replace element={<Navigate to="/" />}/>
</Routes>
)
}
return (
<div className="App">
<header>
<Navbar />
</header>
{routes}
</div>
);
When token !== null you are returning a set of routes that includes a redirect to "/" but doesn't include a route rendering a path for "/", so the app is caught in a render loop redirecting to a non-existent route.
Include a route with path="/" in your "authenticated" path, or redirect to a route that is being rendered, like `"/todos":
const token = useSelector(state => state.authDetails.token);
let routes = (
<Routes>
<Route path='/signUp' exact element={<SignUpForm />} />
<Route path='/' exact element={<LoginForm />} />
<Route path='*' element={<Navigate to="/" replace />} />
</Routes>
);
if (token !== null) {
routes = (
<Routes>
<Route path='/todos' element={<AddTodo/>} />
<Route path='/logout' element={<Logout/>} />
<Route path='*' element={<Navigate to="/todos" replace />} />
</Routes>
);
}
return (
<div className="App">
<header>
<Navbar />
</header>
{routes}
</div>
);
An improved solution would be to create an authenticated wrapper component and wrap the routes you want to protect.
Example:
import { Navigate, Outlet } from 'react-router-dom';
const AuthWrapper = () => {
const token = useSelector(state => state.authDetails.token);
return token !== null ? <Outlet /> : <Navigate to="/" replace />;
};
...
return (
<div className="App">
<header>
<Navbar />
</header>
<Routes>
<Route path='/signUp' element={<SignUpForm />} />
<Route path='/' element={<LoginForm />} />
<Route element={<AuthWrapper />}>
<Route path='/todos' element={<AddTodo />} />
<Route path='/logout' element={<Logout />} />
</Route>
<Route path='*' element={<Navigate to="/" replace />} />
</Routes>
</div>
);
If you need to conditionally redirect to different targets on the wildcard route then create another wrapper to handle the auth check.
Example:
const RedirectWrapper = () => {
const token = useSelector(state => state.authDetails.token);
return <Navigate to={token !== null ? "/todos" : "/" replace />;
};
...
return (
<div className="App">
<header>
<Navbar />
</header>
<Routes>
<Route path='/signUp' element={<SignUpForm />} />
<Route path='/' element={<LoginForm />} />
<Route element={<AuthWrapper />}>
<Route path='/todos' element={<AddTodo />} />
<Route path='/logout' element={<Logout />} />
</Route>
<Route path='*' element={<RedirectWrapper />} />
</Routes>
</div>
);