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

React Native Firebase登出时documentSnapshot.exists报错解决

报错根因

触发这个错误有三个核心问题:

  • 登出操作执行后,UserContext存储的用户对象会被置为null,你没有做前置空值判断就直接访问user.uid,同时Firestore权限随登出失效,之前绑定的实时快照监听器回调拿到的documentSnapshot为null,直接访问.exists属性就会抛出类型错误。
  • 你把dbUser.onSnapshot监听逻辑直接写在组件函数的渲染流程里,没有做监听清理:每次组件重渲染都会新建一个监听器,既会造成内存泄漏,登出后旧的监听器也不会自动销毁,会持续触发回调。
  • 没有处理用户未登录的边界场景,登出后组件依然会尝试拉取需要登录权限的用户文档数据。
修复方案
  1. 引入useEffect钩子管理快照监听器,仅在用户存在、组件挂载时绑定监听,在组件卸载/依赖变化时调用监听器返回的取消方法,销毁旧监听。
  2. 增加前置空值判断:用户对象为空时直接返回默认视图,不执行文档查询逻辑;快照回调里先判断documentSnapshot是否有效,再访问其属性。
  3. 给useEffect添加正确的依赖项,用户ID、展示类型变化时自动重新绑定对应监听器。
修复后完整代码
import React, { useContext, useState, useEffect } from 'react';
import { Text, View } from 'react-native';
import firestore from '@react-native-firebase/firestore';

import styles from './ShowReportCount.styles';
import { UserContext } from '../../../context/UserContext';

const db = firestore();

export default function ShowReportCount({ type }) {
    const [count, setCount] = useState(0);
    const [user] = useContext(UserContext);

    useEffect(() => {
        // 用户未登录时不绑定监听,直接重置计数
        if (!user?.uid) {
            setCount(0);
            return;
        }

        const dbUser = db.collection('users').doc(user.uid);
        // 绑定监听同时拿到取消监听的方法
        const unsubscribe = dbUser.onSnapshot(
            {
                includeMetadataChanges: true,
            },
            (documentSnapshot) => {
                // 先判断快照是否有效,再访问exists属性
                if (documentSnapshot && documentSnapshot.exists) {
                    const userData = documentSnapshot.data();
                    if (type === 'exercise') {
                        setCount(userData.exerciseCount || 0);
                        return;
                    }
                    if (type === 'workout') {
                        setCount(userData.workoutCount || 0);
                        return;
                    }
                }
                // 文档不存在或快照无效时重置计数
                setCount(0);
            },
            (err) => {
                // 监听报错(比如登出后权限失效)时重置计数,避免抛错
                console.log('snapshot error:', err);
                setCount(0);
            }
        );

        // 组件卸载/依赖变化时清理监听
        return () => unsubscribe();
    }, [user?.uid, type]);

    // 用户未登录时直接返回0值视图
    if (!user) {
        return (
            <View>
                <Text style={styles.statCardNumber}>0</Text>
            </View>
        );
    }

    return (
        <View>
            <Text style={styles.statCardNumber}>{count.toString()}</Text>
        </View>
    );
}

注意:原来的组件参数直接写type是不规范的,函数组件的第一个参数是所有props组成的对象,应该解构拿到type属性,否则你之前写的type.type多了一层嵌套,容易出现取值错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 17:30:46