React 路由器 <Redirect> 未根据状态重定向

React router <Redirect> is not redirecting on state

user 不是 null 时,应用应该重定向到 /dashboard。用户在第一个渲染中是 null,但 useEffect() 应该从本地存储中获取和存储用户数据。然后,三元运算符应该触发 <Redirect to='/dashboard' /> 并重定向到仪表板页面。但是,它并没有这样做。每当我刷新时,即使 localStorage 中有数据,它也会一直在 /login 显示登录页面。

我做错了什么或者我该如何解决这个问题?

const App = () => {
    const [user, setUser] = useState(null)

    useEffect(() => {
        const loggedUserJSON = window.localStorage.getItem('loggedUser')

        if (loggedUserJSON) {
            const user = JSON.parse(loggedUserJSON)
            setUser(user)
            noteService.setToken(user.token)
        }
    }, [])

    return (
        <Router>
            <Switch>
                <Route exact path='/'>
                    {user
                        ? <Redirect to='/dashboard' />
                        : <Redirect to='/login' />
                    }
                </Route>
                <Route exact path='/login'>
                    <LogIn />
                </Route>
                <Route exact path='/dashboard'>
                    <Dashboard />
                </Route>
            </Switch>
        </Router>
    )

问题

问题是在初始渲染时 user 状态是 null 并且重定向到“/login”。当您重新加载页面时,如果路径仍然是“/login”,它不会完全匹配“/”并且不会发生重定向,无论user 个州。

解决方案

从 localStorage 初始化状态而不是等待渲染周期。这至少允许您在第一次渲染而不是第二次(或更高版本)重新渲染时检查 user 状态。重新排序 Switch 组件中的路由,以指定它们从 more 具体到 less 具体。重定向应该放在最后,以便其他路径有机会首先匹配和呈现。

const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));

const App = () => {
  const [user] = useState(initializeState());

  useEffect(() => {
    noteService.setToken(user.token);
  }, [user]);

  return (
    <Router>
      <Switch>
        <Route path='/login'>
          <LogIn />
        </Route>
        <Route path='/dashboard'>
          <Dashboard />
        </Route>
        <Redirect to={user ? '/dashboard' : '/login'} />
      </Switch>
    </Router>
  );
};

这并没有完全解决用户已经在子路由上的页面重新加载问题。为此,您 应该 实现一个 ProtectedRoute 组件并将 user 状态作为道具传递。如果有一个 user 对象,它会呈现一个常规的 Route 组件并传递道具,否则它会呈现一个 Redirect 并将用户跳转到登录路由。

const ProtectedRoute = ({ user, ...props }) => user ? (
  <Route {...props} />
) : (
  <Redirect to="/login" />
);

const initializeState = () => JSON.parse(window.localStorage.getItem('loggedUser'));

const App = () => {
  const [user] = useState(initializeState());

  useEffect(() => {
    noteService.setToken(user.token);
  }, [user]);

  return (
    <Router>
      <Switch>
        <Route path='/login'>
          <LogIn />
        </Route>
        <ProtectedRoute path='/dashboard' user={user} >
          <Dashboard />
        </ProtectedRoute>
        <Redirect to={user ? '/dashboard' : '/login'} />
      </Switch>
    </Router>
  );
};

如果您使用的是版本 6,请阅读以下内容:reactrouter.com/docs/en/v6/upgrading/v5

特别是它告诉您删除直接位于 <Switch>.

内的所有 <Redirect> 元素的部分