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

Expo React Native使用Firebase获取认证用户及资料偶发null错误求解

问题根因分析
  • 你对setUser、setProfile加了await,但React的状态更新函数本身不返回Promise,await不会等待状态更新完成就会执行后续的setIsLoading(false),导致偶尔出现导航已经跳转到HomeStack,但profile状态还没更新完成的情况,这就是偶发null的核心原因。
  • 目前仅在Auth状态变化时单次拉取Firestore数据,后续如果用户资料在其他端/场景更新,前端无法同步,需要手动刷新才能拿到新数据。
  • 边界场景未处理:如果是刚注册的新用户,members集合下还没有对应用户uid的文档,profile会一直为null,没有兜底逻辑。
修复方案

将身份认证、资料监听的逻辑全部收敛到AuthenticatedUserProvider中,职责划分更清晰,同时给Firestore添加实时快照监听,资料更新后全局自动同步:

1. 修改 AuthenticatedUserProvider.js

import React, { useState, createContext, useEffect } from 'react';
import { auth, fs } from '../config/firebase';

export const AuthenticatedUserContext = createContext({});

export const AuthenticatedUserProvider = ({ children }) => {
    const [user, setUser] = useState(null);
    const [profile, setProfile] = useState(null);
    const [isLoading, setIsLoading] = useState(true);

    useEffect(() => {
        let unsubscribeProfile = null;
        // 监听Auth状态变化
        const unsubscribeAuth = auth.onAuthStateChanged(async (authenticatedUser) => {
            try {
                // 先清除之前的profile监听,避免内存泄漏
                if (unsubscribeProfile) {
                    unsubscribeProfile();
                    unsubscribeProfile = null;
                }

                if (authenticatedUser) {
                    setUser(authenticatedUser);
                    // 监听对应用户的profile文档变化,实时更新
                    unsubscribeProfile = fs.collection("members")
                        .doc(authenticatedUser.uid)
                        .onSnapshot((snapshot) => {
                            if (snapshot.exists) {
                                setProfile(snapshot.data());
                            } else {
                                setProfile(null);
                                // 可在这里添加新用户无profile的兜底逻辑,比如跳转补全资料页
                            }
                            // 所有数据加载完成后再关闭loading
                            setIsLoading(false);
                        }, (error) => {
                            console.log("拉取用户资料失败:", error);
                            setProfile(null);
                            setIsLoading(false);
                        });
                } else {
                    setUser(null);
                    setProfile(null);
                    setIsLoading(false);
                }
            } catch (error) {
                console.log("身份状态处理失败:", error);
                setUser(null);
                setProfile(null);
                setIsLoading(false);
            }
        });

        // 组件卸载时清除所有监听
        return () => {
            unsubscribeAuth();
            if (unsubscribeProfile) {
                unsubscribeProfile();
            }
        };
    }, []);

    return (
        <AuthenticatedUserContext.Provider value={{ user, profile, isLoading }}>
            {children}
        </AuthenticatedUserContext.Provider>
    );
};

2. 简化 RootNavigator.js

import React, { useContext } from 'react';
import { View, ActivityIndicator } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import * as eva from '@eva-design/eva';
import { ApplicationProvider, IconRegistry } from '@ui-kitten/components';
import { EvaIconsPack } from '@ui-kitten/eva-icons';
import { AuthenticatedUserContext } from './AuthenticatedUserProvider';
import AuthStack from './AuthStack';
import HomeStack from './HomeStack';
import { default as theme } from '../orange-theme.json';

export default function RootNavigator() {
    const { user, isLoading } = useContext(AuthenticatedUserContext);

    if (isLoading) {
        return (
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
                <ActivityIndicator size='large' />
            </View>
        );
    }

    return (
        <>
            <IconRegistry icons={EvaIconsPack} />
            <ApplicationProvider {...eva} theme={{ ...eva.light, ...theme }}>
                <NavigationContainer>
                    {user ? <HomeStack /> : <AuthStack />}
                </NavigationContainer>
            </ApplicationProvider >
        </>
    );
}
额外注意事项
  • 新用户注册完成后,要立即在members集合下创建对应用户uid的文档,避免出现profile为空的情况
  • 可根据业务需求,在HomeStack的页面中加profile为空的兜底占位UI,进一步避免报错
  • 如需要支持离线使用,可开启Firestore的离线持久化功能,无网络时也能读取到缓存的profile数据

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 05:57:02