标题重写为:用户登录时,React Route v6中的受保护路由未更新
P粉297434909
P粉297434909 2023-08-29 15:00:09
0
1
456
<p>我正在尝试登录用户。我有一个登录组件,它接收凭据,然后我使用redux toolkit来存储状态,并且验证和所有操作都在userSlice中完成。我有一个受保护的路由,应该检查用户是否已登录,如果用户未登录,则不应导航到我拥有的食谱页面。当我尝试使用useSelecter hook从受保护的路由组件访问用户时,它在第一次渲染时返回空,但在第二次渲染时确实返回用户,但登录仍然失败。在redux dev tools中,状态被更新得很好。是否有一种方法可以在受保护的路由组件的第一次渲染中获取用户对象?(正如您所看到的,我正在使用useEffect hook,并且有依赖数组)。</p> <p>非常感谢您的帮助。 谢谢。</p> <p>下面是我的代码:</p> <p>Login.js -- 这个文件负责接收凭据,然后使用useDispatch分派一个动作并使用useDispatch更新状态。</p> <pre class="brush:php;toolbar:false;">import React, { useState } from 'react'; import { loginUser } from '../../features/users/userSlice'; import { useSelector, useDispatch } from 'react-redux' import { useNavigate } from 'react-router-dom'; export default function Login() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const user = useSelector(state => state.user.user) const dispatch = useDispatch() const navigate = useNavigate() return ( <div> <input type="text" value={email} onChange={(e) => setEmail(e.target.value)} /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} /> <button type="submit" onClick={() => { dispatch(loginUser({email, password})) navigate('/recipes') }}>submit</button> </div> ) }</pre> <p>ProtectedRoute.js -- 此组件确保如果用户未经身份验证,他将无法登录</p> <pre class="brush:php;toolbar:false;">import React, { useState, useEffect } from "react"; import { Route, Navigate, Outlet } from "react-router-dom"; import { useSelector } from 'react-redux'; export default function ProtectedRoute({ children }) { const [ activeUser, setActiveUser ] = useState(false) const user = useSelector((state) => state.user); useEffect(() => { if (!user.isLoading) { user.success ? setActiveUser(true) : setActiveUser(false) console.log('active user: ' + activeUser) } }, [user]) return ( activeUser ? <Outlet /> : <Navigate to="/login"/> ) }</pre> <p>app.js -- 此组件包含所有路由,包括受保护的路由。</p> <pre class="brush:php;toolbar:false;">import React from "react"; import Recipes from "./components/recipes/recipes"; import Login from "./components/users/Login"; import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; import ProtectedRoute from "./utils.js/ProtectedRoute"; const App = () => { return ( <div> <BrowserRouter> <Routes> <Route path="/login" index element={<Login />} /> <Route path="/" element={<Navigate replace to="/login" />}/> <Route element={<ProtectedRoute />}> <Route element={<Recipes/>} path="/recipes" /> </Route> </Routes> </BrowserRouter> </div> ); }; export default App;</pre> <p>userSlice.js -- 由于我使用redux toolkit,因此我有不同功能的slices。这里有reducers,其中有用户状态。</p> <pre class="brush:php;toolbar:false;">import { createSlice, createAsyncThunk } from "@reduxjs/toolkit"; import axios from "axios"; const loginUrl = 'http://localhost:5000/api/login'; const signupUrl = 'http://localhost:5000/api/signup'; export const loginUser = createAsyncThunk('user/loginUser', async (data) => { const response = await axios.post(loginUrl, data); return response; }) export const signupUser = createAsyncThunk('user/signupUser', async (data) => { const response = await axios.post(signupUrl, data); return response; }) const initialState = { user: {}, isLoading: true } const userSlice = createSlice({ name: 'user', initialState, reducers: { getPassword: (state, action) => { const password = action.payload console.log(password) } }, extraReducers: { [loginUser.pending]: (state) => { state.isLoading = false }, [loginUser.fulfilled]: (state, action) => { state.isLoading = false state.user = action.payload.data }, [loginUser.rejected]: (state) => { state.isLoading = false }, [signupUser.pending]: (state) => { state.isLoading = false }, [signupUser.fulfilled]: (state, action) => { state.isLoading = false state.user = action.payload.data }, [signupUser.rejected]: (state) => { state.isLoading = false }, } }) export const { getPassword } = userSlice.actions export default userSlice.reducer;</pre></p>
P粉297434909
P粉297434909

全部回复(1)
P粉043432210

问题在于登录处理程序同时发出了两个动作。

onClick={() => {
  dispatch(loginUser({ email, password })); // <-- 登录用户
  navigate('/recipes');                     // <-- 立即导航
}}

在用户进行身份验证之前,导航到受保护的路由。

为了解决这个问题,登录处理程序应该等待成功的身份验证,然后重定向到所需的路由。

const loginHandler = async () => {
  try {
    const response = await dispatch(loginUser({ email, password })).unwrap();
    if (/* 检查响应条件 */) {
      navigate("/recipes", { replace: true });
    }
  } catch (error) {
    // 处理任何错误或被拒绝的 Promise
  }
};

...


onClick={loginHandler}

更多详细信息请参见处理 Thunk 结果

您还可以简化ProtectedRoute逻辑,不需要额外的重新渲染,只需获取正确的输出进行渲染。所有路由保护状态都可以从选择的 redux 状态中派生。

export default function ProtectedRoute() {
  const user = useSelector((state) => state.user);

  if (user.isLoading) {
    return null; // 或者加载指示器/旋转器等
   }

  return user.success ? <Outlet /> : <Navigate to="/login" replace />;
}
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板