如何在 React 组件中结合使用 useNavigate 和 useEffect 以在组件渲染时有条件地重定向?

How to use useNavigate in combination with useEffect in React component to conditionally redirect when component renders?

在我的“主要”组件(包含我的“路由器”和所有“路由”的组件)中,我想从我的“后端”获取当前登录的用户。

然后我要么将这个用户存储在我的全局“上下文”中(使他们可以从所有“页面”访问,允许保护某些“路由”等),如果有的话,或者我想如果当前没有用户登录,则重定向到我的“登录页面”(参见下面示例代码中的 switch 语句)。

但是,这不起作用,因为 navigate() 似乎仅当对它的调用源自 <Router> 组件时才起作用。有没有一种简单且“最佳实践”的方法来解决这个问题?非常感谢任何提示或想法!谢谢!

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;

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

是的!您可以在 ReactTree 中将 Router 移至更高位置...高于您要使用 navigate 函数的组件 and 提供您要更新的上下文值。

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>