React Firebase认证项目useContext刷新瞬时为null导致私有路由访问异常
问题根因
AuthContext.js中的useEffect配置存在错误- 依赖项错误添加了
user,会导致每次user值变化时都重新注册身份状态监听器,造成重复执行 - 清理函数直接调用了
unsub(),会导致监听器注册完成后立刻被销毁,后续身份状态变化无法被正常监听
- 依赖项错误添加了
- 缺少身份校验等待状态:Firebase的
onAuthStateChanged是异步回调,页面刷新后初始user为null,在Firebase返回用户身份信息前的空窗期,AuthGuard会直接判定为未登录,触发跳转到登录页的逻辑
修复方案
1. 修改AuthContext.js
- 新增
loading状态记录身份校验进度,初始值为true - 修正useEffect的依赖项为空数组,仅在组件挂载时注册一次监听器
- 修正清理函数写法,仅在组件卸载时销毁监听器
- 身份校验完成后同步更新
user和loading状态,将loading暴露到context中
import React, { useContext, useEffect, useState } from "react"; import { firebaseAuth } from "../firebase"; import { createUserWithEmailAndPassword, signOut, signInWithEmailAndPassword, onAuthStateChanged, } from "firebase/auth"; const AuthContext = React.createContext(); export const useAuth = () => { return useContext(AuthContext); }; export const AuthContextProvider = ({ children }) => { const [user, setUser] = useState(null); // 新增loading状态 const [loading, setLoading] = useState(true); console.log("Auth Context User:", user); useEffect(() => { const unsub = onAuthStateChanged(firebaseAuth, (user) => { setUser(user); // 身份校验完成,关闭loading setLoading(false); }); // 修正清理函数,仅在组件卸载时调用unsub return unsub; // 依赖项改为空数组,仅挂载执行一次 }, []); function SignUp(email, password) { return createUserWithEmailAndPassword(firebaseAuth, email, password); } function Logout() { return signOut(firebaseAuth); } function Login(email, password) { return signInWithEmailAndPassword(firebaseAuth, email, password); } const value = { logout: Logout, signup: SignUp, login: Login, user, // 暴露loading到context loading }; return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; };
2. 修改AuthGuard.js
- 新增loading判断,身份校验完成前渲染加载占位内容,不触发跳转逻辑
import React from "react"; import { Navigate, useLocation } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; const AuthGuard = ({ children }) => { let { user, loading } = useAuth(); let location = useLocation(); console.log("user", user); // 校验中返回加载状态,避免误跳转 if(loading) { // 可替换为自定义加载动画组件 return <div>加载中...</div> } if (!user) { return <Navigate to="/" state={{ from: location }} />; } return children; }; export default AuthGuard;
内容的提问来源于stack exchange,提问作者Tirth Trivedi
相关产品推荐
相关产品推荐

