You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 22:35:15