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

React Native (Expo)中如何等待Firebase onSnapshot加载数据避免重复请求

核心解决思路

把onSnapshot的首次回调包装为Promise,替代单独的get()请求,既可以在AppLoading中等待加载完成,又不会重复触发数据拉取。


改造步骤

1. 改造订阅Hook,返回初始化Promise

修改useInitFirebaseSubscriptions,将首次onSnapshot回调的完成状态通过Promise暴露给外部:

import { useRef, useEffect } from 'react';
import { useDispatch } from 'react-redux';

export function useInitFirebaseSubscriptions() {
    const dispatch = useDispatch();
    // 存储Promise的resolve、reject方法
    const initResolveRef = useRef();
    const initRejectRef = useRef();
    // 生成初始化Promise供外部等待
    const initPromise = useRef(new Promise((resolve, reject) => {
        initResolveRef.current = resolve;
        initRejectRef.current = reject;
    })).current;

    useEffect(() => {
        console.log('[CONTROLLER]: subscribed to Firebase');
        let isFirstLoad = true;
        const unsubscribe = firestore()
            .collection('users')
            .onSnapshot(
                (querySnapshot) => {
                    dispatch(action.fetch(querySnapshot));
                    // 仅首次加载完成时resolve Promise
                    if (isFirstLoad) {
                        isFirstLoad = false;
                        initResolveRef.current();
                    }
                },
                (error) => {
                    console.log(error);
                    // 首次加载失败时reject Promise
                    if (isFirstLoad) {
                        isFirstLoad = false;
                        initRejectRef.current(error);
                    }
                }
            );
        
        return () => {
            unsubscribe();
            console.log('[CONTROLLER]: unsubscribed from Firebase');
        };
    }, []);

    return initPromise;
}

2. 改造Manager组件,移除冗余拉取逻辑

删除原来的useFetchData调用,直接等待订阅的初始化Promise完成:

const Manager = (props) => {
    //STATE
    const [init, setInit] = useState(false);

    //HOOKS - 拿到订阅的初始化Promise
    const firebaseInitPromise = useInitFirebaseSubscriptions();

    //FUNCTIONS
    async function onInit() {
        console.log('[MANAGER]: loading app...');
        // 等待订阅首次加载完成即可,无需额外拉取数据
        await firebaseInitPromise;
    }
    function onFinishedInit() {
        console.log('[MANAGER]: ...app loading successfull!');
        setInit(true);
    }

    //RETURN
    if (!init) {
        return <AppLoading startAsync={onInit} onFinish={onFinishedInit} onError={console.warn} />;
    } else {
        return props.children;
    }
};
export default Manager;

3. 删除冗余的useFetchData相关代码即可。


效果验证

改造后控制台只会输出一次[USER_REDUCER]: fetched data,不会重复请求,同时AppLoading会正常等待数据加载完成后再渲染主页面,不会出现闪动。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 09:06:03