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

Next.js中useEffect为何重复渲染?如何仅获取最终认证状态?

解决Next.js中React Context认证状态多次返回的问题

你的问题核心是组件初始化时先返回默认的isAuthenticated: false,随后useEffect执行更新状态,导致子组件(如ProtectedRoutes)接收到两次状态值。以下是两种针对性的解决方案:

方案一:直接初始化正确的认证状态

由于tokenServices.getToken()是同步操作(从本地存储读取token),可以直接在useState初始化时计算认证状态,无需依赖useEffect触发二次更新。

修改后的AuthProvider代码:

"use client";
import React, { createContext, useState, useEffect } from "react";
import { tokenServices } from "./services";
import { usePathname } from "next/navigation";

type JWTContextType = {
  isAuthenticated: boolean;
};

const AuthContext = createContext<JWTContextType>({
  isAuthenticated: false,
});

const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  // 初始化时直接根据token设置认证状态,避免默认值的中间渲染
  const [isAuthenticated, setIsAuthenticated] = useState(() => 
    !!tokenServices.getToken()
  );
  const pathname = usePathname();

  useEffect(() => {
    // 路径切换时重新校验token,确保状态与当前token一致
    const hasAccess = !!tokenServices.getToken();
    setIsAuthenticated(hasAccess);
  }, [pathname]);

  return (
    <AuthContext.Provider value={{ isAuthenticated }}>
      {children}
    </AuthContext.Provider>
  );
};

export { AuthContext, AuthProvider };

方案二:添加加载状态屏蔽中间值

如果你的token校验存在异步场景(比如需要调用API验证token有效性),可以通过加载状态,在认证结果确定前不渲染子组件,避免子组件接收到中间状态。

修改后的AuthProvider代码:

"use client";
import React, { createContext, useState, useEffect } from "react";
import { tokenServices } from "./services";
import { usePathname } from "next/navigation";

type JWTContextType = {
  isAuthenticated: boolean;
};

const AuthContext = createContext<JWTContextType>({
  isAuthenticated: false,
});

const AuthProvider = ({ children }: { children: React.ReactNode }) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [isLoading, setIsLoading] = useState(true);
  const pathname = usePathname();

  useEffect(() => {
    const checkAuthentication = () => {
      const hasAccess = !!tokenServices.getToken();
      setIsAuthenticated(hasAccess);
      setIsLoading(false);
    };

    checkAuthentication();
  }, [pathname]);

  // 加载中时显示占位内容(可替换为自定义加载组件)
  if (isLoading) {
    return <div>验证权限中...</div>;
  }

  return (
    <AuthContext.Provider value={{ isAuthenticated }}>
      {children}
    </AuthContext.Provider>
  );
};

export { AuthContext, AuthProvider };

配套的ProtectedRoutes示例

确保权限校验组件只在最终状态下执行跳转逻辑:

"use client";
import { useContext } from "react";
import { AuthContext } from "./GuestGuard";
import { useRouter } from "next/navigation";

export const ProtectedRoutes = ({ children }: { children: React.ReactNode }) => {
  const { isAuthenticated } = useContext(AuthContext);
  const router = useRouter();

  if (!isAuthenticated) {
    router.push("/login");
    return null;
  }

  return children;
};

原问题原因说明

你之前的代码中,useState初始值为false,组件挂载时会先渲染并传递这个默认值给子组件;随后useEffect在挂载后执行,更新认证状态,触发二次渲染,导致子组件接收到两次状态值。通过上述方案,要么直接初始化正确状态,要么通过加载状态屏蔽中间过程,就能确保子组件只拿到最终的认证结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 23:10:11