skip to Main Content

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


  1. So the issue you are facing is that the <Navigate /> is called on initial render. This is because your state is initially false 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

    const [checkedLogin, setCheckedLogin] = useState(false);
    

    within your useEffect set the setLogin(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

    {isLoggedIn ? children : (checkedLogin ? <Navigate to='/login' />: <></>)}
    

    Does this make sense?

    Login or Signup to reply.
  2. 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’s useSelector 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:

    import React, { useEffect } from 'react';
    import { Outlet, Navigate } from 'react-router-dom';
    
    import LoginService from '../../services/Login/login.service';
    
    const ProtectedRoute: React.FC = ({ children }) => {
      useEffect(() => {
        async function checkAccess() {
          const loginService = new LoginService();
          const canAccess = await loginService.checkUserAccess();
          localStorage.setItem('isLoggedIn', canAccess.toString());
        }
    
        checkAccess();
      }, []);
    
      const isLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
    
      return isLoggedIn ? <>{children}</> : <Navigate to="/login" />;
    };
    
    export default ProtectedRoute;
    

    I hope this helps.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search