Home > Mobile >  How to use useNavigate in combination with useEffect in React component to conditionally redirect wh
How to use useNavigate in combination with useEffect in React component to conditionally redirect wh

Time:01-23

In my "main" component (the one that is cointaining my "router" and all my "routes") I would like to fetch the currently logged in user from my "backend".

Then I would either store this user in my global "context" (to make them accessible from all "pages", allow to protect certain "routes", etc.) if there is one, or I would like to redirect to my "login page" (see switch statement in the sample code below) if there is no user currently logged in.

However, that does not work since navigate() appears to work only if the call to it originates from within the <Router> component. Is there a simple and "best-practice" way to solve this? Any hint or idea is highly appreciated! Thanks!

import React, { useState, useMemo, useEffect } from 'react';
import {
  BrowserRouter as Router,
  Routes,
  Route,
  useNavigate,
} from 'react-router-dom';
import { User, Context, UserContext } from '../lib/UserContext';
import Home from '../pages/Home';
import Test from '../pages/Test';
import Login from '../pages/Login';
import Register from '../pages/Register';
import axios from 'axios';
const StatusCodes = require('../statusCodes');

const AppRouter = () => {
  const navigate = useNavigate();
  const [user, setUser] = useState<User | null>(null);
  const context = useMemo<Context>(
    () => ({ user: user, setUser: setUser }),
    [user, setUser]
  );

  useEffect(() => {
    axios({
      method: 'GET',
      withCredentials: true,
      url: 'http://localhost:4000/user',
    }).then((res) => {
      switch (res.data.code) {
        case StatusCodes.UserIsLoggedIn:
          console.log('A user is logged in. Context set to this user.');
          context.setUser({
            lastName: res.data.user.lastName,
            firstName: res.data.user.firstName,
            email: res.data.user.email,
            role: res.data.user.role,
          });
          break;
        case StatusCodes.NoUserIsLoggedIn:
          console.log('No user is logged in. Redirect to login page.');
          navigate('/login');
          break;
        default:
          console.log('Status code not recognized. Redirect to test page.');
          navigate('/test');
          break;
      }
    });
  }, []);

  return (
    <UserContext.Provider value={context}>
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/test" element={<Test />} />
          <Route path="/login" element={<Login />} />
          <Route path="/register" element={<Register />} />
        </Routes>
      </Router>
    </UserContext.Provider>
  );
};

export default AppRouter;

CodePudding user response:

Is there a simple and "best-practice" way to solve this?

Yes! You can move the Router higher in the ReactTree... higher than the component you want to use the navigate function and provide a Context value you want to update.

const AppRouter = () => {
  const navigate = useNavigate();
  const [user, setUser] = useState<User | null>(null);
  const context = useMemo<Context>(
    () => ({ user: user, setUser: setUser }),
    [user, setUser]
  );

  ...

  return (
    <UserContext.Provider value={context}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/test" element={<Test />} />
        <Route path="/login" element={<Login />} />
        <Route path="/register" element={<Register />} />
      </Routes>
    </UserContext.Provider>
  );
};

...

<Router>
  <AppRouter />
</Router>
  •  Tags:  
  • Related