I have an application made in ReactJS that uses Vite (Typescript). My application’s route file is being configured as follows:
index.tsx
:
import { createBrowserRouter } from "react-router-dom";
import routes from "./routes";
import ContractPage from "@pages/ContractPage";
import Login from "@pages/Login";
import ForgotPassword from "@pages/ForgotPassword";
import ChangePassword from "@pages/ChangePassword";
import ProtectedRoute from "./ProtectedRoute";
const Routes = createBrowserRouter([
{ path: '/', element: <Login /> },
{ path: routes.LOGIN, element: <Login /> },
{ path: routes.RECOVER_PASSWORD, element: <ForgotPassword /> },
{ path: routes.CHANGE_PASSWORD, element: <ChangePassword /> },
//Protected routes below
{ path: routes.CONTRACTS, element: <ProtectedRoute children={<ContractPage />} /> },
]);
export default Routes;
Inside my ProtectedRoute.tsx
I have the following logic:
import React, { useState, useEffect } from 'react';
import { Outlet, Navigate } from 'react-router-dom';
import LoginService from '../../services/Login/login.service';
interface ProtectedRouteProps {
children: React.ReactNode;
}
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
async function checkAccess() {
const loginService = new LoginService();
const canAccess = await loginService.checkUserAccess();
console.log('canAccess', canAccess);
setIsLoggedIn(canAccess);
}
checkAccess();
}, []);
return (
<>
{alert(isLoggedIn)}
{isLoggedIn ? children : <Navigate to='/login' />}
</>
);
};
export default ProtectedRoute;
It is important to note that the checkUserAccess()
method is returning true
.
When I try to access the contracts route, an alert
is generated stating that the isLoggedIn
state is set to false
, where a short time later I receive a message in the console
claiming that it was set to true
.
The problem is that the user has already been redirected back to the login screen.
How can I solve this problem?
2
Answers
So the issue you are facing is that the
<Navigate />
is called on initial render. This is because your state is initiallyfalse
and the{isLoggedIn ? children : <Navigate to='/login' />}
will fall back to the Navigate part.I would recommend introducing an additional state which keeps the information about the async login check like
within your
useEffect
set thesetLogin(true);
as soon as the async login check finished (either with a true or false no matter).Then for the implementation in the return do something like
Does this make sense?
The inconsistency arises because the state toggles between its initial value and updated state. To address this in the
ProtectedRoute
file, you can bypass state management and directly store the logged-in user’s state in local storage or global state. This can be achieved using tools like Redux’suseSelector
or directly accessing local storage. This approach ensures consistency by persisting the logged-in state across sessions, allowing you to render the component based on the stored state.Here’s how you could modify your ProtectedRoute component to utilize local storage for storing the logged-in state:
I hope this helps.