登录重定向至首页后如何实时更新登录状态
我在Node.js后端使用HttpOnly Cookie做身份认证,无法通过document.cookies访问该Cookie。根布局中通过useEffect调用后端认证状态API,页面加载时若Cookie有效会显示用户名,但登录后通过react-router action重定向到首页时,根布局未重载、useEffect也未触发,必须手动刷新才能看到登录状态。
现有代码片段
根布局的useEffect逻辑
const [userName, setUserName] = useState(); const [userId, setUserId] = useState(); useEffect(() => { const showMe = async () => { let res = await axios.get( "http://localhost:5000/api/v1/users/showMe", { withCredentials: true, } ); console.log("Entered in root useEffect"); setUserName(res.data.user.name); setUserId(res.data.user.userId); }; showMe(); }, [userName, userId]);
Login路由的action逻辑
export const action = async ({ request, params }) => { console.log(params); const data = await request.formData(); const loginData = { email: data.get("email"), password: data.get("password"), }; console.log(loginData); let url = "http://localhost:5000/api/v1/auth/login"; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(loginData), credentials: "include", }); if (response.status === 422) { console.log(response); return response; } if (!response.ok) { console.log(response); throw json({ message: "Could not login." }, { status: 500 }); } return redirect("/"); };
路由配置
{ path: "/", element: <RootLayout />, children: [ { index: true, element: <Home /> }, { path: "login", element: <Login></Login>, action: action, }, { path: "logout", element: <Logout></Logout>, action: logoutAction, }, ], }
解决方案
方案1:利用React Router的useNavigation监听路由状态
通过useNavigation钩子监听路由跳转状态,当路由从登录页跳转完成后,重新调用认证API更新状态。
修改根布局代码:
import { useNavigation } from 'react-router-dom'; // ... 原有状态定义 const navigation = useNavigation(); useEffect(() => { const fetchUser = async () => { try { const res = await axios.get( "http://localhost:5000/api/v1/users/showMe", { withCredentials: true } ); setUserName(res.data.user.name); setUserId(res.data.user.userId); } catch (err) { // 处理未登录/认证失效的情况 setUserName(null); setUserId(null); } }; // 路由跳转完成(状态变为idle)时触发API调用 if (navigation.state === 'idle') { fetchUser(); } }, [navigation.state]); // 依赖路由状态变化,触发重新执行
方案2:在登录Action中返回用户数据,通过useActionData传递
修改登录Action,让后端在登录成功时直接返回用户信息,前端通过useActionData获取数据并更新状态,无需二次调用API。
第一步:修改Login Action
export const action = async ({ request, params }) => { const data = await request.formData(); const loginData = { email: data.get("email"), password: data.get("password"), }; const url = "http://localhost:5000/api/v1/auth/login"; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(loginData), credentials: "include", }); if (response.status === 422) { return response; } if (!response.ok) { throw json({ message: "Could not login." }, { status: 500 }); } // 登录成功后获取用户数据(需后端接口支持返回) const userData = await response.json(); // 返回用户数据并触发重定向 return json(userData, { status: 200, headers: { Location: "/" } }); };
第二步:根布局中读取Action数据更新状态
import { useActionData } from 'react-router-dom'; // ... 原有状态定义 const actionData = useActionData(); useEffect(() => { // 如果是登录成功返回的用户数据,直接更新状态 if (actionData?.user) { setUserName(actionData.user.name); setUserId(actionData.user.userId); } else { // 初始化时调用API获取状态 const fetchUser = async () => { try { const res = await axios.get( "http://localhost:5000/api/v1/users/showMe", { withCredentials: true } ); setUserName(res.data.user.name); setUserId(res.data.user.userId); } catch (err) { setUserName(null); setUserId(null); } }; fetchUser(); } }, [actionData]);
方案3:使用全局状态管理(Context API)
创建全局AuthContext,将用户状态存入全局上下文,登录成功后直接更新全局状态,根布局自动感知状态变化并渲染。
第一步:创建AuthContext
// AuthContext.js import { createContext, useContext, useState, useEffect } from 'react'; import axios from 'axios'; const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); // 初始化时获取用户状态 useEffect(() => { const fetchUser = async () => { try { const res = await axios.get("http://localhost:5000/api/v1/users/showMe", { withCredentials: true }); setUser(res.data.user); } catch (err) { setUser(null); } }; fetchUser(); }, []); // 登录后更新用户状态的方法 const loginUser = (userData) => { setUser(userData); }; // 登出方法 const logoutUser = () => { setUser(null); }; return ( <AuthContext.Provider value={{ user, loginUser, logoutUser }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext);
第二步:在路由入口包裹AuthProvider
// App.js import { AuthProvider } from './AuthContext'; import { RouterProvider } from 'react-router-dom'; import router from './router'; function App() { return ( <AuthProvider> <RouterProvider router={router} /> </AuthProvider> ); }
第三步:Login组件中更新全局状态
// Login.jsx import { useActionData, useNavigation } from 'react-router-dom'; import { useAuth } from './AuthContext'; const Login = () => { const actionData = useActionData(); const { loginUser } = useAuth(); const navigation = useNavigation(); useEffect(() => { if (actionData?.user) { loginUser(actionData.user); navigation.navigate('/'); } }, [actionData, loginUser, navigation]); // ... 登录表单渲染逻辑 };
第四步:根布局直接读取全局状态
// RootLayout.jsx import { useAuth } from './AuthContext'; import { Outlet } from 'react-router-dom'; const RootLayout = () => { const { user } = useAuth(); return ( <div> <header> {user ? <span>欢迎,{user.name}</span> : <a href="/login">登录</a>} </header> <Outlet /> </div> ); };
内容的提问来源于stack exchange,提问作者Shaharin Ahmed
相关产品推荐
相关产品推荐

