React Native Firebase登出时documentSnapshot.exists报错解决
报错根因
触发这个错误有三个核心问题:
- 登出操作执行后,
UserContext存储的用户对象会被置为null,你没有做前置空值判断就直接访问user.uid,同时Firestore权限随登出失效,之前绑定的实时快照监听器回调拿到的documentSnapshot为null,直接访问.exists属性就会抛出类型错误。 - 你把
dbUser.onSnapshot监听逻辑直接写在组件函数的渲染流程里,没有做监听清理:每次组件重渲染都会新建一个监听器,既会造成内存泄漏,登出后旧的监听器也不会自动销毁,会持续触发回调。 - 没有处理用户未登录的边界场景,登出后组件依然会尝试拉取需要登录权限的用户文档数据。
修复方案
- 引入
useEffect钩子管理快照监听器,仅在用户存在、组件挂载时绑定监听,在组件卸载/依赖变化时调用监听器返回的取消方法,销毁旧监听。 - 增加前置空值判断:用户对象为空时直接返回默认视图,不执行文档查询逻辑;快照回调里先判断
documentSnapshot是否有效,再访问其属性。 - 给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
相关产品推荐
相关产品推荐

