useAuth自定义Hook在用户已认证时仍返回isAuthenticated:false排查
问题根源
你遇到的核心问题是自定义Hook的状态是组件隔离的:每次调用useAuth()都会创建独立的isAuthenticated状态实例。PrivateRoutes组件和其他调用useAuth的组件各自持有自己的状态,所以即使某个地方通过getUser把状态设为true,PrivateRoutes里的状态还是初始的false——因为它是完全独立的另一个实例。
解决方案:用React Context共享全局认证状态
要让所有组件共享同一个认证状态,需要把状态提升到React Context中,确保所有组件访问的是同一个状态源。
1. 改写Auth.tsx,创建AuthContext和Provider
import { useState, useEffect, createContext, useContext } from 'react'; import axios from 'axios'; const instance = axios.create({ baseURL: 'http://localhost:8000', }); // 创建AuthContext,默认值用于类型提示 const AuthContext = createContext({ isAuthenticated: false, isLoading: true, // 新增加载状态,避免异步请求时误跳转 getUser: () => {}, }); // 创建AuthProvider组件,包裹需要共享认证状态的组件 export const AuthProvider = ({ children }) => { const [isAuthenticated, setIsAuthenticated] = useState(false); const [isLoading, setIsLoading] = useState(true); const getUser = async () => { try { const response = await instance.get('/user', { withCredentials: true }); if (typeof response.data.user === 'object' && response.data.user !== null) { console.log("authenticated"); setIsAuthenticated(true); } else { console.log("not authenticated"); setIsAuthenticated(false); } } catch (err) { // 处理请求失败的情况,比如网络错误、未授权 console.error("获取用户信息失败:", err); setIsAuthenticated(false); } finally { // 请求完成后结束加载状态 setIsLoading(false); } }; useEffect(() => { getUser(); }, []); return ( <AuthContext.Provider value={{ isAuthenticated, isLoading, getUser }}> {children} </AuthContext.Provider> ); }; // 自定义Hook,用于在组件中获取Context export const useAuth = () => { return useContext(AuthContext); };
2. 修改PrivateRoutes.tsx,处理加载状态并使用共享状态
import React from 'react'; import { useAuth } from './Auth'; import { Outlet, Navigate } from 'react-router-dom'; const PrivateRoutes = () => { const { isAuthenticated, isLoading } = useAuth(); // 请求还在进行时,显示加载中(避免直接跳转到登录页) if (isLoading) { return <div>加载中...</div>; // 可以替换成你的自定义加载组件 } return isAuthenticated ? <Outlet /> : <Navigate to="/login" />; }; export default PrivateRoutes;
3. 在根组件中包裹AuthProvider
确保整个应用(至少是需要认证的路由部分)被AuthProvider包裹,比如在App.tsx或者路由配置文件中:
import { AuthProvider } from './Auth'; import { BrowserRouter, Routes, Route } from 'react-router-dom'; import PrivateRoutes from './PrivateRoutes'; import Login from './Login'; import Dashboard from './Dashboard'; function App() { return ( <AuthProvider> <BrowserRouter> <Routes> <Route path="/login" element={<Login />} /> {/* 私有路由嵌套在PrivateRoutes下 */} <Route element={<PrivateRoutes />}> <Route path="/dashboard" element={<Dashboard />} /> {/* 其他私有路由 */} </Route> </Routes> </BrowserRouter> </AuthProvider> ); } export default App;
额外说明
- 新增
isLoading状态是为了避免在异步请求还未完成时,因为初始的isAuthenticated: false而错误跳转到登录页。 - 加入了
try/catch处理请求失败的情况,比如用户Cookie过期、网络错误等场景,避免未处理的异常导致页面卡住。 - 现在所有调用
useAuth()的组件都会拿到同一个isAuthenticated状态,状态更新时所有组件都会同步更新。
内容的提问来源于stack exchange,提问作者Juan Bilardi
相关产品推荐
相关产品推荐

