I'm building a MERN Web App and I'm going through an issue: when I login with an existing account, after calling an API to the backend and handling the request, it returns the user properties and the JWT Token, I save it and then (using navigate function) I redirect the User to the homepage. It works fine but it doesn't when i relaod the page. I mean in the first load of the page the login works fine and so does the navigate function, but when I reload the page and enter the login data, it does login but does not redirect me to the homepage. I hope it is understandable! This is the snippet of the code which makes the API call and then the redirect:
Login.js
const handleSubmit = async (event) => {
event.preventDefault()
try {
const res = await axios.post(WEB_URL `/login`, { email, password })
// console.log(res.data);
const { token } = res.data.token;
localStorage.setItem('jwt', token);
// console.log('logged as', res.data.user.name);
navigate("/");
}
catch (error) {
console.log(error);
navigate("/login");
}
};
App.js
const PrivateRoute = () => {
const token = localStorage.getItem('jwt');
return !!token;
};
function App() {
return (
<Router>
<Navbar />
<Routes>
<Route path="/" element={PrivateRoute() ? <Home /> : <Navigate replace to="/login" />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
</Routes>
</Router>
);
}
CodePudding user response:
Issue
The issue here is that when App
renders it calls PrivateRoute
the function and it correctly returns false
since no user is logged in and the <Navigate>
component is rendered.
The router is correctly navigated to "/login"
and the Login
component is rendered and the user authenticates and is navigated back to "/"
. Because no state or props or parent component to App
was updated the App
component doesn't rerender and doesn't re-check the token in localStorage. This means the <Navigate>
component is still rendered and the user is bounced back to "/login"
.
Solution
Make PrivateRoute
an actual React component so the component is rendered/rerendered when the route changes, and so it can recheck the token in localStorage.
Example:
import { Navigate, Outlet } from 'react-router-dom';
const PrivateRoute = () => {
const token = localStorage.getItem("jwt");
return !!token ? <Outlet /> : <Navigate replace to="/login" />;
};
Render PrivateRoute
on a layout route that renders the home page.
<Routes>
<Route element={<PrivateRoute />}>
<Route path="/" element={<Home />} />
</Route>
<Route path="/login" element={<Login />} />
</Routes>