React页面刷新后userToken验证失效跳转登录页问题解决
React 页面刷新后私有路由跳转到登录页问题
开发React应用时,已配置私有路由和基于角色的权限路由,登录后系统运行正常,但页面刷新(重新渲染)时会被跳转到登录页,尽管token仍保存在localStorage中。checkLogin方法可能未正确调用或生效,首次加载时useAuth中的userToken存在,但渲染后变为null并触发跳转。
私有路由组件代码
const PrivateRoutes = () => { const {userToken} = useAuth(); return userToken ? <Outlet /> : <Navigate to="/login" replace />; } export default PrivateRoutes; function AppRoutes() { return ( <Routes> <Route element={<Login />} path="/login" /> <Route element={<Register />} path="/register" /> <Route element={<NoMatch />} path="*" /> <Route element={<PrivateRoutes />}> <Route element={<MainLayout />} path='/'> <Route element={<MyProfile />} path='/myprofile' /> <Route element={<ChangePassword />} path='/changepassword' /> <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ClientUserList} />} path="/client/user/list" /> <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ClientUserCreate} />} path="/client/user/create" /> <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ClientUserEdit} />} path="/client/user/edit/:id" /> <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ClientUserShow} />} path="/client/user/show/:id" /> <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ManageInfo} />} path="/manage" /> <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={ManageEdit} />} path="/manage/edit" /> <Route element={<ProtectedRoute roles={[ROLES.CLIENT]} component={CategoryList} />} path="/category/list" /> </Route> </Route> </Routes> ) }
AuthContext 代码
const AuthContext = createContext({}); const AuthProvider = ({ children }) => { const [userToken, setUserToken] = useState(null); const [userInfo, setUserInfo] = useState(null); const navigate = useNavigate(); const checkLogin = async () => { console.log("checking login info"); const tokenInfo = JSON.parse(localStorage.getItem("userToken")); if (tokenInfo != null) { setUserToken(tokenInfo); var userData = await accountService.getInfo(); if (userData.result > 0) { localStorage.setItem("userInfo", JSON.stringify(userData)); setUserInfo(userData); } else { localStorage.removeItem("userToken"); localStorage.removeItem("userInfo"); setUserToken(null); setUserInfo(null); navigate("/login"); } } else { setUserToken(null); } }; const doLogin = (tokenData) => { console.log("token",tokenData); if (tokenData.result > 0) { localStorage.setItem("userToken", JSON.stringify(tokenData)); setUserToken(tokenData); navigate("/"); getInfo(); // 注意:此处getInfo未定义,存在逻辑漏洞 } }; return ( <AuthContext.Provider value={{ userToken, userInfo, checkLogin, doLogin }}> {children} </AuthContext.Provider> ); }; export const useAuth = () => useContext(AuthContext);
ProtectedRoute 组件代码
function ProtectedRoute({ component: Component, roles, ...rest }) { const { userToken } = useAuth(); const token= userToken.data.token; const userRole = getRole(token); if (!userToken) { return <Navigate to="/login" replace />; } if (roles && roles.indexOf(userRole) === -1) { return <Navigate to="/myprofile" replace />; } return <Component {...rest} />; }
问题原因及解决方案
1. 未初始化调用checkLogin
页面刷新时,AuthProvider的userToken初始值为null,但没有自动触发checkLogin读取localStorage中的token。需在AuthProvider中用useEffect在组件挂载时执行检查:
const AuthProvider = ({ children }) => { // ... 原有状态与方法 useEffect(() => { checkLogin(); }, []); // ... 返回Provider };
2. 异步检查时的过早跳转
checkLogin是异步方法,页面初始渲染时userToken仍为null,私有路由会直接跳转登录页。需添加加载状态,等待检查完成再判断:
// 修改AuthProvider,添加加载状态 const AuthProvider = ({ children }) => { const [userToken, setUserToken] = useState(null); const [userInfo, setUserInfo] = useState(null); const [isLoading, setIsLoading] = useState(true); const navigate = useNavigate(); const checkLogin = async () => { setIsLoading(true); const tokenInfo = JSON.parse(localStorage.getItem("userToken")); if (tokenInfo != null) { setUserToken(tokenInfo); try { const userData = await accountService.getInfo(); if (userData.result > 0) { localStorage.setItem("userInfo", JSON.stringify(userData)); setUserInfo(userData); } else { localStorage.removeItem("userToken"); localStorage.removeItem("userInfo"); setUserToken(null); setUserInfo(null); navigate("/login"); } } catch (err) { console.error("获取用户信息失败:", err); localStorage.removeItem("userToken"); setUserToken(null); navigate("/login"); } } else { setUserToken(null); } setIsLoading(false); }; useEffect(() => { checkLogin(); }, []); return ( <AuthContext.Provider value={{ userToken, userInfo, isLoading, checkLogin, doLogin }}> {children} </AuthContext.Provider> ); };
修改PrivateRoutes:
const PrivateRoutes = () => { const { userToken, isLoading } = useAuth(); if (isLoading) { return <div>加载中...</div>; // 替换为实际加载组件 } return userToken ? <Outlet /> : <Navigate to="/login" replace />; }
修改ProtectedRoute:
function ProtectedRoute({ component: Component, roles, ...rest }) { const { userToken, isLoading } = useAuth(); if (isLoading) { return <div>加载中...</div>; } if (!userToken) { return <Navigate to="/login" replace />; } // 先判断userToken存在,避免访问data时报错 const token = userToken.data.token; const userRole = getRole(token); if (roles && roles.indexOf(userRole) === -1) { return <Navigate to="/myprofile" replace />; } return <Component {...rest} />; }
3. 修复doLogin中的未定义方法
doLogin里的getInfo()未定义,替换为checkLogin或直接实现用户信息获取逻辑:
const doLogin = (tokenData) => { console.log("token",tokenData); if (tokenData.result > 0) { localStorage.setItem("userToken", JSON.stringify(tokenData)); setUserToken(tokenData); navigate("/"); // 替换为已定义的checkLogin checkLogin(); } };
内容的提问来源于stack exchange,提问作者Goktug
相关产品推荐
相关产品推荐

