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

页面刷新时useEffect不执行,如何持久化React Context用户数据?

问题分析与解决方案

问题本质

你的问题属于人为错误,并非Firebase或React的预期行为:

  • 刷新后Context变为null/undefined,是因为组件初始渲染时user状态未初始化,且onAuthStateChanged是异步操作,子组件会先拿到初始空值,之后才会更新。
  • 你认为useEffect未触发,大概率是onAuthStateChanged的回调执行时机稍晚,或是console日志被覆盖——空依赖的useEffect在组件挂载(包括刷新后)一定会执行。

修复步骤

1. 修复状态初始化与订阅清理

首先修正user状态的初始值,并清理Firebase的认证监听,避免内存泄漏:

import React, { useState, useEffect } from "react";
import "./App.css";
import { Routes, Route, useNavigate, useLocation } from "react-router-dom";
import { onAuthStateChanged, signOut } from "firebase/auth";
import { auth, db } from "./config/firebase";
import { doc, getDoc } from "firebase/firestore";

//Components
import Login from "./containers/login/login";
import Test from "./containers/Test";
import PrimaryNavBar from "./components/primarynavbar/primarynavbar";
export const UserContext = React.createContext(null); // 设置Context默认值为null

const App = () => {
  const [user, setUser] = useState(null); // 初始化为null,避免传递undefined
  const navigate = useNavigate();
  const location = useLocation(); // 获取当前路由,避免重复跳转

  useEffect(() => {
    console.log("running auth check effect");
    // 保存Firebase监听的取消函数
    const unsubscribe = onAuthStateChanged(auth, async (userAuthData) => {
      if (userAuthData) {
        try {
          const userDoc = await getDoc(doc(db, "users", userAuthData.uid));
          if (userDoc.exists()) {
            const userInfo = userDoc.data();
            const combinedUser = { ...userAuthData, ...userInfo };
            setUser(combinedUser);
            // 仅当当前不在目标页面时跳转
            if (location.pathname !== "/auctions") {
              navigate("/auctions");
            }
          } else {
            console.log("用户信息未在Firestore中找到");
            setUser(null);
            navigate("/login");
          }
        } catch (error) {
          console.log("获取用户数据失败:", error);
          setUser(null);
          navigate("/login");
        }
      } else {
        setUser(null);
        // 仅当当前不在登录页时跳转
        if (location.pathname !== "/login") {
          navigate("/login");
        }
      }
    });

    // 组件卸载时取消监听,防止内存泄漏
    return () => unsubscribe();
  }, [navigate, location]); // 添加依赖项,符合React Hooks规范

  const logoutHandler = () => {
    signOut(auth)
      .then(() => {
        setUser(null);
        navigate("/login");
      })
      .catch((error) => {
        console.log("登出失败:", error);
      });
  };

  return (
    <UserContext.Provider value={user}>
      <Routes>
        <Route path="/login" element={<Login />} />
        <Route path="/" element={<PrimaryNavBar logout={logoutHandler} />}>
          <Route path="auctions" element={<Test />}></Route>
        </Route>
      </Routes>
    </UserContext.Provider>
  );
};

export default App;

2. 可选:添加本地持久化提升体验

如果希望刷新后快速显示用户数据,可将用户信息存入localStorage:

  • 保存用户数据时同步存入本地:
    const combinedUser = { ...userAuthData, ...userInfo };
    setUser(combinedUser);
    localStorage.setItem("userData", JSON.stringify(combinedUser));
    
  • 初始状态优先读取本地缓存:
    const [user, setUser] = useState(() => {
      const savedUser = localStorage.getItem("userData");
      return savedUser ? JSON.parse(savedUser) : null;
    });
    
  • 登出时清除本地缓存:
    const logoutHandler = () => {
      signOut(auth)
        .then(() => {
          setUser(null);
          localStorage.removeItem("userData");
          navigate("/login");
        })
        .catch((error) => {
          console.log("登出失败:", error);
        });
    };
    

3. 子组件中处理空状态

在使用Context的子组件中,添加加载状态或空值判断,避免报错:

import { useContext } from "react";
import { UserContext } from "../App";

const Test = () => {
  const user = useContext(UserContext);
  
  if (!user) {
    return <div>加载中...</div>;
  }

  return <div>欢迎,{user.name}</div>;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 00:45:41