Home > Net >  React - redirect to login page when not authenticated
React - redirect to login page when not authenticated

Time:12-15

This is my primary react file:

// App.tsx
const App: FC = () => {

  const isLoggedIn: boolean = localStorage.getItem('logged_user') !== null;

  return (
    <BrowserRouter>
        <Routes>
          <Route path="/main" element={isLoggedIn ? <Main/> : <Navigate to='/login'/>}/>
          <Route path="/about" element={isLoggedIn ? <About/> : <Navigate to='/login'/>}/>
          <Route path="/login" element={<Login/>}/>
        </Routes>
    </BrowserRouter>
  );
}

export default App;

After logging in, I store the user in local storage.

I want to achieve the behaviour of redirections to the /login page when the user is not authenticated (when it is not stored in localstorage).

Generally, the above approach works but only sometimes. Sometimes, when I go to '/main', I would get redirected to '/login' even though I was logged in. I assume this is caused by React's nature of re-renders.

How can I approach this?

CodePudding user response:

I'm guessing the redirect to "/login" works the first time, you log in, then try to navigate to "/main" or "/about" and are getting redirected back to "/login" until you do something like a page reload and read any persisted "logged_user" state and then get stuck not being to log out and get redirected back to "/login".

You should store the isLoggedIn value in local state, initialized from localStorage, and provide a way within the app to toggle the state. Use an useEffect hook to persist local state changes back to localStorage.

Example:

const App: FC = () => {
  const [isLoggedIn, setIsLoggedIn] = useState<boolean>(
    () => localStorage.getItem('logged_user') !== null
  );

  useEffect(() => {
    localStorage.setItem('logged_user', JSON.stringify(isLoggedIn));
  }, [isLoggedIn]);

  
  const logIn = () => setIsLoggedIn(true);

  // pass this callback to components you want to allow logging out
  // it will update the local state and then get persisted
  const logOut = () => setIsLoggedIn(false);

  return (
    <BrowserRouter>
      <Routes>
        <Route path="/main" element={isLoggedIn ? <Main/> : <Navigate to='/login'/>}/>
        <Route path="/about" element={isLoggedIn ? <About/> : <Navigate to='/login'/>}/>
        <Route path="/login" element={<Login onLogIn={logIn} />}/>
      </Routes>
    </BrowserRouter>
  );
}

CodePudding user response:

The way I like to do it, is to create a <PrivateLink /> component, so that it is clear that whatever route is nested requires an authenticated user.

const PrivateRoute = (props: { children: React.ReactNode }): JSX.Element => {
  const { children } = props
  const isLoggedIn: boolean = localStorage.getItem('logged_user') !== null;
  const location = useLocation()

  return isLoggedIn ? (
    <>{children}</>
  ) : (
    <Navigate
      replace={true}
      to="/login"
      state={{ from: `${location.pathname}${location.search}` }}
    />
  )
}

Then in your App.tsx

const App: FC = () => (
  <BrowserRouter>
      <Routes>
        <Route path="/main" element={<PrivateRoute> <Main/> </PrivateRoute>}/>
        <Route path="/about" element={<PrivateRoute> <About/> </PrivateRoute>}/>
        <Route path="/login" element={<Login/>}/>
      </Routes>
  </BrowserRouter>
)

As an added extra, the from variable that is passed to the state in the <PrivateRoute /> component, allows you to redirect the user back to whichever page they came from after logging in.

  • Related