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

Next.js 13组件与页面数据共享及登录状态获取异常问题

问题:Next.js + React Context共享Firebase登录状态始终返回false

我正在用Next.js和React Hook Context实现网站账号用户状态的跨组件/页面共享,但每次访问数据时,isLoggedIn始终返回false。

相关代码

src/layout.tsx

"use client";

import "./globals.css";
import Header from "./components/Header";
import Footer from "./components/Footer";

import React, { useState, useEffect } from "react";
import { usePathname, useRouter } from "next/navigation";
import AuthProvider from "./contexts/AuthContext";
import { useIsLogged } from "./hooks/IsLogged";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const [menu, setMenu] = useState(false);

  const path = usePathname();
  const router = useRouter();

  const { isLoggedIn } = useIsLogged();

  useEffect(() => {
    if (isLoggedIn) {
      if (path === "/admin/login") {
        router.push("/admin/blog");
      }
    } else {
      if (path.includes("/admin")) {
        router.push("/admin/login");
      }
    }
  }, [isLoggedIn]);

  return (
    <html lang="en">
      <body className={`${menu && "overflow-hidden sm:overflow-auto"}`}>
        <AuthProvider>
          <Header menu={menu} setMenu={setMenu} />
          {children}
          <Footer />
        </AuthProvider>
      </body>
    </html>
  );
}

/src/contexts/AuthContext.tsx

import React, { useState, useEffect, createContext, ReactNode } from "react";
import { auth } from "@/services/firebase";

export const AuthContext = createContext(
  {} as {
    isLoggedIn: boolean;
    setLoggedIn: React.Dispatch<React.SetStateAction<boolean>>;
  },
);

export default function AuthProvider({ children }: { children: ReactNode }) {
  const [isLoggedIn, setLoggedIn] = useState(false);

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((user) => { // When Firebase Auth State Change
      if (user) setLoggedIn(true);
    });
    return () => {
      unsubscribe();
    };
  }, []);

  return (
    <AuthContext.Provider value={{ isLoggedIn, setLoggedIn }}>
      {children}
    </AuthContext.Provider>
  );
}

/src/hooks/IsLogged.ts

import { useContext } from "react";
import { AuthContext } from "@/app/contexts/AuthContext";

export const useIsLogged = () => {
  const value = useContext(AuthContext);
  return value;
};

我使用Firebase Authentication进行用户认证。


问题原因分析

  1. Context Provider作用范围错误:RootLayout中直接调用了useIsLogged,但AuthProvider只包裹了内部的Header、children和Footer,RootLayout本身不在Provider范围内,导致获取的是Context的初始默认值false,且后续状态更新无法同步。
  2. Firebase状态处理不完整:仅在用户登录时设置isLoggedIn: true,未处理用户登出的情况,状态不会自动更新为false。
  3. 初始状态与实际状态不同步:页面加载时Firebase需要时间获取当前用户状态,初始useState(false)可能与实际状态不符,导致初始值错误。

解决方案

1. 调整AuthProvider的包裹范围

将需要使用Context的逻辑抽离到子组件,让AuthProvider包裹整个需要访问Context的部分:

"use client";

import "./globals.css";
import Header from "./components/Header";
import Footer from "./components/Footer";

import React, { useState, useEffect } from "react";
import { usePathname, useRouter } from "next/navigation";
import AuthProvider from "./contexts/AuthContext";
import { useIsLogged } from "./hooks/IsLogged";

// 抽离需要访问Context的内容到子组件
const LayoutContent = ({ children }: { children: React.ReactNode }) => {
  const [menu, setMenu] = useState(false);
  const path = usePathname();
  const router = useRouter();
  const { isLoggedIn, isLoading } = useIsLogged();

  useEffect(() => {
    if (isLoading) return; // 加载中不执行路由跳转

    if (isLoggedIn) {
      path === "/admin/login" && router.push("/admin/blog");
    } else {
      path.includes("/admin") && router.push("/admin/login");
    }
  }, [isLoggedIn, isLoading, path, router]);

  return (
    <>
      <Header menu={menu} setMenu={setMenu} />
      {children}
      <Footer />
    </>
  );
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <AuthProvider>
          <LayoutContent>{children}</LayoutContent>
        </AuthProvider>
      </body>
    </html>
  );
}

2. 完善Firebase Auth状态处理

添加加载状态,同时处理用户登出的情况:

import React, { useState, useEffect, createContext, ReactNode } from "react";
import { auth } from "@/services/firebase";

export const AuthContext = createContext(
  {} as {
    isLoggedIn: boolean;
    isLoading: boolean; // 添加加载状态
    setLoggedIn: React.Dispatch<React.SetStateAction<boolean>>;
  },
);

export default function AuthProvider({ children }: { children: ReactNode }) {
  const [isLoggedIn, setLoggedIn] = useState(false);
  const [isLoading, setIsLoading] = useState(true); // 初始处于加载状态

  useEffect(() => {
    const unsubscribe = auth.onAuthStateChanged((user) => {
      setLoggedIn(!!user); // 有用户则为true,无用户则为false
      setIsLoading(false); // 获取到状态后结束加载
    });
    return () => unsubscribe();
  }, []);

  // 加载中可显示加载组件,避免错误路由跳转
  if (isLoading) {
    return <div>加载中...</div>;
  }

  return (
    <AuthContext.Provider value={{ isLoggedIn, isLoading, setLoggedIn }}>
      {children}
    </AuthContext.Provider>
  );
}

3. 更新Hook(可选)

确保Hook返回完整的Context值(当前代码已满足,无需修改):

import { useContext } from "react";
import { AuthContext } from "@/app/contexts/AuthContext";

export const useIsLogged = () => {
  const value = useContext(AuthContext);
  return value;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:23:10