You need to enable JavaScript to run this app. React+Firebase Firestore+Reactfire中useFirestoreDocData触发无限循环问题
优惠活动
大模型
产品
解决方案
定价
更多

React+Firebase Firestore+Reactfire中useFirestoreDocData触发无限循环问题

问题根因

无限重渲染的核心原因是每次组件渲染都会生成全新的Firestore文档引用,触发useFirestoreDocData不断重新订阅,进而反复触发重渲染形成循环:

  • 组件顶层执行useFirestore().collection('GameLobbies').doc()时,无参调用doc()会每次生成一个带随机ID的全新文档引用对象
  • useFirestoreDocData只要检测到传入的文档引用变化,就会重新执行订阅逻辑,返回新的status和data,这两个状态变化会触发组件重渲染
  • 重渲染后又会生成新的文档引用,进入下一轮循环,形成死循环

修复方案

用useMemo把文档引用的创建逻辑包裹,保证整个组件生命周期内只会生成一次引用,破坏循环触发的条件。同时补全useEffect的依赖项,避免闭包陷阱。

修改后完整代码:

import * as React from 'react';
import { useFirestore, useFirestoreDocData, useUser } from 'reactfire';
import 'firebase/firestore';
import 'firebase/auth';
import { useEffect, useState, useMemo } from 'react';
import { Link, useHistory } from 'react-router-dom';


interface ICreateGameProps {
}

interface IGameLobbyDoc {
    playerOne: string;
    playerTwo: string | null;
}

const CreateGame: React.FunctionComponent<ICreateGameProps> = (props) => {
    const user = useUser();
    const firestore = useFirestore();
    // 用useMemo保证只生成一次文档引用
    const gameLobbyDocRef = useMemo(() => {
        return firestore.collection('GameLobbies').doc()
    }, [firestore])

    const { status, data } = useFirestoreDocData<IGameLobbyDoc>(gameLobbyDocRef);

    const [newGameId, setNewGameId] = useState('')
    const history = useHistory();

    // 补全依赖项,确保用户加载完成后能正常创建文档
    useEffect(() => {
        async function createGameLobby() {
            if (!user.data?.uid) return
            const gl: IGameLobbyDoc = {
                playerOne: user.data.uid,
                playerTwo: null
            }
            await gameLobbyDocRef.set(gl)
            setNewGameId(gameLobbyDocRef.id)
        }
            
        createGameLobby()

        return () => {
            gameLobbyDocRef.delete();
        }
    }, [user.data?.uid, gameLobbyDocRef])

    // 增加用户加载态,避免无意义的渲染
    if (user.status === 'loading') return <p>加载中...</p>

    return <>
        <h2>Gameid : {newGameId}</h2>
        <p>Waiting for second player to join...</p>
        <Link to="/">Go Back</Link>
    </>
};

export default CreateGame;

额外优化建议

  • 如果不需要在文档创建后实时监听文档变化,可把订阅逻辑放到文档创建成功之后再执行,避免多余的空文档查询
  • 可增加文档创建失败的异常捕获逻辑,提升稳定性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 09:45:05