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

ReactJS中Firebase onAuthStateChanged触发两次的问题咨询

问题分析与修复方案

你的onAuthStateChanged触发两次,核心是代码里的订阅管理逻辑错误,具体问题如下:

1. 提前取消订阅的错误操作

你在onAuthStateChanged的回调开头就调用了unsubscribe(),但Firebase Auth的状态监听机制是:初始化时会先返回一个临时的null状态(表示身份还在确认中),等本地缓存的用户信息加载完成后,会再次触发回调返回用户对象。这种提前取消订阅的操作不仅打断了正常的状态确认流程,还可能因为Firebase内部的调度逻辑,导致两次回调都被执行后才完成取消,最终出现重复触发的现象。

2. 异常捕获范围错误

外层的try/catch只能捕获onAuthStateChanged订阅时的同步错误,无法捕获回调内部异步操作(比如getIdToken(true))的异常,导致认证错误无法被正确捕获和处理。

3. 状态更新时序混乱

getIdToken(true)是异步操作,在未完成前没有状态锁定,若回调重复触发,会导致多次异步请求同时进行,进一步引发状态更新冲突。


修复后的代码

import React, { useEffect, useState } from 'react';
import { getAuth, onAuthStateChanged, User } from 'firebase/auth';
import { useNavigate } from 'react-router-dom';
import { RiseLoader } from 'react-spinners';
import ErrorPage from '../Pages/ErrorPage';

export interface IAuthRouteProps {
    children: React.ReactNode;
    onTokenReceived: (token: string) => void;
}

const AuthRoute: React.FunctionComponent<IAuthRouteProps> = ({ children, onTokenReceived }) => {
  
    const auth = getAuth();
    const navigate = useNavigate();
    const [loading, setLoading] = useState(true);
    const [errorMessage, setErrorMessage] = useState("");
    
    useEffect(() => {
        setErrorMessage("");
        // 创建订阅并保存取消函数
        const unsubscribe = onAuthStateChanged(auth, async (user: User | null) => {
            try {
                if (user) {
                    console.log('triggering routes -@c1');
                    const token = await user.getIdToken(true);
                    setLoading(false); 
                    onTokenReceived(token); 
                } else {
                    console.log('unauthorized');
                    setLoading(false);
                    navigate('/login');
                }
            } catch (error) {
                setErrorMessage("Authentication issue");
                setLoading(false);
            }
        });

        // 组件卸载时取消订阅,避免内存泄漏和重复触发
        return () => unsubscribe();
    }, []); 

    if (loading) return <div className="mainHome">
        <RiseLoader className="progressStyle" color="#5AA5F5" size={16} />
        {errorMessage.length > 1 ? <ErrorPage message={''} code={0} /> :<></>}
    </div>;
  
    return <>{children}</>;
};
  
export default AuthRoute;

关键修复说明

  • 正确管理订阅生命周期:移除回调内的unsubscribe(),改为在useEffect的清理函数中执行,确保组件卸载时正确取消监听,同时让Firebase完成完整的身份确认流程。
  • 调整异常捕获位置:将try/catch移到回调内部,捕获异步操作的错误,保证错误能被及时处理。
  • 统一状态更新:无论用户是否登录,都在逻辑执行完毕后设置loading为false,避免页面长时间处于加载状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 03:26:08