標題重寫為:使用者登入時,React Route v6中的受保護路由未更新
P粉297434909
P粉297434909 2023-08-29 15:00:09
0
1
474
<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 } 從 '../../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 />;
}
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板