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

React从Cookie取Token时Context状态无法更新的问题

问题分析与解决方案

你的核心问题出在Cookie读取逻辑错误、初始状态异步更新导致路由判断提前触发,以及缺少Token有效性验证这几个点上。以下是针对性的修复方案:


1. 修复AuthProvider的状态初始化逻辑

原代码中Cookie读取放在组件外部,会导致闭包捕获旧值,且未处理Cookie的空格问题,同时缺少Token过期验证。修改后的代码如下:

export const AuthProvider = ({children}: AuthContextProviderType) => {
    const [user, setUser] = useState<AuthUser | null>(null);
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const [isValidated, setIsValidated] = useState(false);
    const [isLoading, setIsLoading] = useState(true); // 新增加载状态,避免初始false误判

    useEffect(() => {        
        const validateAuth = () => {
            // 正确解析Cookie:处理分号后的空格,转为键值对对象
            const cookieMap = document.cookie.split("; ").reduce((acc, cookie) => {
                const [key, value] = cookie.split("=");
                acc[key.trim()] = value.trim();
                return acc;
            }, {} as Record<string, string>);

            const authToken = cookieMap["authorization"];
            if (!authToken) {
                setIsAuthenticated(false);
                setUser(null);
                setIsLoading(false);
                return;
            }

            try {
                // 解码并验证Token是否过期
                const decoded = jwt.decode(authToken) as { exp?: number } & AuthUser;
                if (decoded?.exp && Date.now() >= decoded.exp * 1000) {
                    // Token过期,清除Cookie并重置状态
                    document.cookie = "authorization=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
                    setIsAuthenticated(false);
                    setUser(null);
                } else {
                    setIsAuthenticated(true);
                    setUser(decoded);
                }
            } catch (err) {
                // Token格式无效,重置状态
                setIsAuthenticated(false);
                setUser(null);
            }
            setIsLoading(false);
        };

        validateAuth();
    }, []);

    const values = {
        user,
        setUser,
        isAuthenticated,
        setIsAuthenticated,
        isValidated,
        setIsValidated,
        isLoading,
    }
 
    return (
        <AuthContext.Provider value={values}>
            {/* 加载中显示占位,避免路由提前跳转 */}
            {isLoading ? <div>加载中...</div> : children}
        </AuthContext.Provider>
    )
}

2. 优化受保护路由逻辑

原方案中受保护路由依赖组件内部状态,无法同步Context的全局状态。改为直接读取Context状态,并等待验证完成:

// 独立的ProtectedRoute组件
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
    const { isAuthenticated, isLoading } = useContext(AuthContext);

    if (isLoading) {
        return <div>加载中...</div>;
    }

    // 验证通过则渲染页面,否则跳转到登录页
    return isAuthenticated ? children : <Navigate to="/login" replace />;
};

// 路由使用示例
return(
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/request-access" element={<RequestAccess />} />
    <Route path="/login" element={<Login />} />
    {/* 受保护路由直接使用ProtectedRoute包裹 */}
    <Route path="/verify-email" element={
      <ProtectedRoute>
        <VerifyEmail/>
      </ProtectedRoute>
    } />
  </Routes>
)

3. 完善登录时的Cookie设置

原登录逻辑中Cookie未指定路径和过期时间,可能导致Cookie无法跨页面读取。修改后:

await axios.post(url + "login", payload)
  .then(res => {
      const authHeader = res.headers.authorization;
      if (!authHeader) throw new Error("未获取到授权Token");
      
      const token = authHeader.split(" ")[1];
      const decoded = jwt.decode(token) as { User: AuthUser };
      
      context.setUser(decoded.User);
      context.setIsAuthenticated(true);
      
      // 设置Cookie:指定路径、过期时间(7天)和SameSite属性
      const expires = new Date();
      expires.setDate(expires.getDate() + 7);
      document.cookie = `authorization=${token}; expires=${expires.toUTCString()}; path=/; SameSite=Lax`;
      
      resolve('Success');
  })
  .catch(err => {     
      console.log(err);           
      reject("登录失败")
  })

关键注意事项

  • 避免闭包陷阱:不要在组件外部读取Cookie,确保每次useEffect执行时都重新读取最新的Cookie
  • Token有效性验证:不要仅判断Token存在,必须检查过期时间,防止使用失效Token
  • 加载状态控制:页面初始化时先显示加载,等待AuthProvider完成Token验证后再渲染路由,避免初始false状态导致的错误跳转
  • Cookie规范:设置Cookie时必须指定path=/确保全站可读取,添加SameSite=Lax提升安全性

内容的提问来源于stack exchange,提问作者Ford Burgess

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:45:23