I want to render routes based on the user login status.So, I made a function to check if the user is authenticated on the backend using axios and call that function in useEffect in App component and store the respone in the useState. And then I use condition in the Route component's element. If the user is authenticated, redirect the user to home page. If not, redirect to login page. But the problem is when I try to enter the route from url bar, I always get redirected to login page even I'm authenticated.
these are my codes(I removed some unrelated codes to look cleaner)
function App() {
const navigate = useNavigate();
const [isLoggedIn, setIsLoggedIn] = useState("NOT_LOGGED_IN");
const [user, setUser] = useState({});
const verifyLogin = async () => {
const res = await axios({
url: "http://localhost:5000/isloggedin",
method: "get",
withCredentials: true
})
if (res.data.isLoggedIn && isLoggedIn === "NOT_LOGGED_IN") {
setIsLoggedIn("LOGGED_IN");
setUser(res.data.user)
} else if (!res.data.isLoggedIn && isLoggedIn === "LOGGED_IN") {
setIsLoggedIn("NOT_LOGGED_IN");
setUser({})
}
}
useEffect(() => {
verifyLogin()
}, [])
return (
<div className="App">
<Routes>
<Route path='/' element={isLoggedIn === "LOGGED_IN" ? <Home isLoggedIn={isLoggedIn} user={user} /> : <Navigate to="/login" />} exact />
<Route path='/register' element={<Register handleRegister={handleRegister} registerError={registerError} />} />
<Route path='/register/:userId/info' element={isLoggedIn === "LOGGED_IN" ? <RegisterInfo handleRegister={handleRegister} registerError={registerError} /> : <Navigate to={"/register"} />} />
<Route path='/login' element={<Login isLoggedIn={isLoggedIn} handleLogin={handleLogin} logout={logout} />} />
</Routes>
</div >
);
}
I'm sorry if my writing made you confused. I'm not so good at English.
CodePudding user response:
Issue
The issue is that your initial isLoggedIn
state matches your unauthenticated state. When the app initially loads and you are trying to access any route the app uses this "NOT_LOGGED_IN"
initial isLoggedIn
state value and determines the user is not logged in and redirects accordingly.
Solution
The common solution is to start from an "indeterminant" state that is neither authenticated nor unauthenticated and conditionally render nothing, or a loading indicator, etc... anything but the routed component or the redirect.
Example:
function App() {
const navigate = useNavigate();
const [isLoggedIn, setIsLoggedIn] = useState(); // <-- initially undefined
const [user, setUser] = useState({});
useEffect(() => {
const verifyLogin = async () => {
const res = await axios({
url: "http://localhost:5000/isloggedin",
method: "get",
withCredentials: true
});
if (res.data.isLoggedIn && isLoggedIn === "NOT_LOGGED_IN") {
setIsLoggedIn("LOGGED_IN");
setUser(res.data.user);
} else if (!res.data.isLoggedIn && isLoggedIn === "LOGGED_IN") {
setIsLoggedIn("NOT_LOGGED_IN");
setUser({});
}
};
verifyLogin();
}, []);
if (isLoggedIn === undefined) { // <-- check if undefined
return null; // or loading indicator, etc...
}
return (
<div className="App">
<Routes>
<Route
path='/'
element={isLoggedIn === "LOGGED_IN"
? <Home isLoggedIn={isLoggedIn} user={user} />
: <Navigate to="/login" />
}
/>
<Route
path='/register'
element={(
<Register
handleRegister={handleRegister}
registerError={registerError}
/>
)}
/>
<Route
path='/register/:userId/info'
element={isLoggedIn === "LOGGED_IN"
? (
<RegisterInfo
handleRegister={handleRegister}
registerError={registerError}
/>
)
: <Navigate to={"/register"} />
}
/>
<Route
path='/login'
element={(
<Login
isLoggedIn={isLoggedIn}
handleLogin={handleLogin}
logout={logout}
/>
)}
/>
</Routes>
</div >
);
}
Further Suggestions
- Abstract the
isLoggedIn
state and auth verification into a React context. - Abstract the protected routing logic into a wrapper or layout component.
This makes your code quite a bit more DRY.