React Router V6中使用<Outlet/>和useEffect刷新时登出问题求助
React Router V6 私有路由JWT验证异步问题解决
问题描述
在React Router V6的PrivateRoutes组件中,通过useEffect调用axios验证JWT Token时,由于axios的异步特性,组件会先使用auth的初始状态(logged: null)进行判断,导致页面刷新时直接跳转到登录页,而此时验证请求还未完成,后续返回的正确登录状态无法生效。另外,尝试在异步的verify函数中返回<Outlet/>或<Navigate/>也无法渲染内容,因为异步函数的返回值不会被React作为组件输出处理。
原代码
const PrivateRoutes = () => { const { auth, setAuth } = useContext(AuthContext); // 认证状态 const verify = async () => { Axios.defaults.withCredentials = true; const res = await Axios.get("http://localhost:3001/verify"); const data = res.data; console.log("data from jwt", data); setAuth({ logged: data.logged, id: data.user_id, name: data.name, }); }; useEffect(() => { verify(); }, []); console.log("private", auth); return auth.logged ? <Outlet /> : <Navigate to="/login" />; }; export default PrivateRoutes;
刷新时控制台输出顺序
private {logged: null, name: '', id: ''} data from jwt {id: 3, name: 'mariela', logged: true}
无效的修改尝试
尝试在异步的verify函数中直接返回组件,但组件无渲染内容,因为异步函数的返回值无法被React识别为渲染输出:
const PrivateRoutes = () => { const { auth, setAuth } = useContext(AuthContext); const verify = async () => { Axios.defaults.withCredentials = true; const res = await Axios.get("http://localhost:3001/verify"); const data = res.data; setAuth({ logged: data.logged, id: data.usu_id, name: data.nombre, }); if (data.logged) { return <Outlet />; } else { return <Navigate to="/login" />; } }; useEffect(() => { verify(); }, []); }; export default PrivateRoutes;
正确解决方案
核心思路是添加一个加载状态,在验证请求完成前不进行路由判断,避免初始状态导致的错误跳转:
- 在
PrivateRoutes组件中新增loading状态,标记验证请求是否完成 - 在
verify函数中,请求完成后更新loading状态 - 渲染时先判断
loading,完成后再根据auth状态决定渲染内容
修改后的代码:
import { useState, useEffect, useContext } from 'react'; import { Outlet, Navigate } from 'react-router-dom'; import { AuthContext } from './AuthContext'; // 根据你的实际路径调整 import Axios from 'axios'; const PrivateRoutes = () => { const { auth, setAuth } = useContext(AuthContext); const [loading, setLoading] = useState(true); // 新增加载状态 const verify = async () => { Axios.defaults.withCredentials = true; try { const res = await Axios.get("http://localhost:3001/verify"); const data = res.data; setAuth({ logged: data.logged, id: data.user_id, name: data.name, }); } catch (error) { // 验证失败(如Token过期、无效),设置为未登录状态 setAuth({ logged: false, id: '', name: '' }); console.error('Token验证失败:', error); } finally { // 无论请求成功或失败,都结束加载状态 setLoading(false); } }; useEffect(() => { verify(); }, []); // 加载中时返回null(或自定义加载组件) if (loading) { return null; // 也可以替换为加载组件:return <div>加载中...</div>; } // 加载完成后根据auth状态判断路由 return auth.logged ? <Outlet /> : <Navigate to="/login" replace />; }; export default PrivateRoutes;
说明
- 加载状态
loading确保组件在验证请求完成前不会进行路由跳转,避免初始auth状态导致的错误登出 - 加入
try/catch捕获验证请求的异常,处理Token过期、无效等情况,保证状态正确更新 replace属性添加到<Navigate>,避免用户点击返回按钮回到验证过程中的页面
内容的提问来源于stack exchange,提问作者Jorgefilemon
相关产品推荐
相关产品推荐

