React Native Firestore:如何结合.where()与.onSnapshot()监听数据更新?
问题:Firestore结合where查询监听数据变更,FlatList不更新且重复key错误
问题背景
我实现了一个从Firestore数据库加载数据的FlatList,目前可执行所有CRUD操作,但编辑条目后,FlatList未同步更新(数据库中数据已变更)。怀疑原因是未使用.onSnapshot(),但不清楚如何将.where()查询与监听数据变更结合。
原代码如下:
export const Coach = () => { const navigation = useNavigation(); const [user, setUser] = useState(); const [userName, setUserName] = useState(''); const [workoutIds, setWorkoutIds] = useState([]); const [workouts, setWorkouts] = useState([]); const userRef = firestore().collection('Users'); const workoutRef = firestore().collection('Workouts'); // Setting the user state auth().onAuthStateChanged(userInstance => { if (userInstance) { setUser(userInstance); } }); // Getting coach id's from firestore - Started out at individual workout id's useEffect(() => { if (user) { const subscriber = userRef.doc(user.uid).onSnapshot(userSnap => { if (userSnap) { setUserName(userSnap.data().Name); setWorkoutIds(userSnap.data().Workouts); } }); return () => subscriber(); } }, [user]); // using the list of coach id's to get workouts useEffect(() => { if (workoutIds.length != 0) { let workoutList = []; workoutRef .where(firestore.FieldPath.documentId(), 'in', workoutIds) .get() .then(query => { query.forEach(snap => { workoutList.push({...snap.data(), key: snap.id}); }); setWorkouts(workoutList); }); } }, [workoutIds]);
我尝试在查询中添加.onSnapshot(),修改前后代码对比:
修改前
// using the list of coach id's to get workouts useEffect(() => { if (workoutIds.length != 0) { let workoutList = []; workoutRef .where(firestore.FieldPath.documentId(), 'in', workoutIds) .get() .then(query => { query.forEach(snap => { workoutList.push({...snap.data(), key: snap.id}); }); setWorkouts(workoutList); }); } }, [workoutIds]);
修改后
// using the list of coach id's to get workouts useEffect(() => { if (workoutIds.length != 0) { let workoutList = []; workoutRef .where(firestore.FieldPath.documentId(), 'in', workoutIds) .onSnapshot(query => { query.forEach(snap => { workoutList.push({...snap.data(), key: snap.id}); }); setWorkouts(workoutList); }); } }, [workoutIds]);
但视图仍无法即时更新,且出现重复key错误。
修复方案
问题根源
- 重复key错误:每次
onSnapshot触发时,workoutList没有重置,旧数据持续累加,导致同一key多次出现。 - 监听器未清理:
useEffect未返回订阅取消函数,当workoutIds变化时,旧的监听器仍在运行,会引发重复更新和内存泄漏。 - 数据更新逻辑冗余:用
forEach+push的方式构建数组容易出错,改用map更简洁可靠。
修复后的代码
export const Coach = () => { const navigation = useNavigation(); const [user, setUser] = useState(); const [userName, setUserName] = useState(''); const [workoutIds, setWorkoutIds] = useState([]); const [workouts, setWorkouts] = useState([]); const userRef = firestore().collection('Users'); const workoutRef = firestore().collection('Workouts'); auth().onAuthStateChanged(userInstance => { if (userInstance) { setUser(userInstance); } }); useEffect(() => { if (user) { const subscriber = userRef.doc(user.uid).onSnapshot(userSnap => { if (userSnap.exists) { const data = userSnap.data(); setUserName(data.Name); setWorkoutIds(data.Workouts || []); } }); return () => subscriber(); } }, [user]); useEffect(() => { // 当workoutIds为空时直接清空列表 if (workoutIds.length === 0) { setWorkouts([]); return; } // 创建快照订阅 const subscriber = workoutRef .where(firestore.FieldPath.documentId(), 'in', workoutIds) .onSnapshot(querySnapshot => { // 每次快照触发时重新构建数组,避免累加 const updatedWorkouts = querySnapshot.docs.map(snap => ({ ...snap.data(), key: snap.id })); setWorkouts(updatedWorkouts); }); // 返回取消订阅函数,清理旧监听器 return () => subscriber(); }, [workoutIds]); // ... 其他组件逻辑(比如FlatList渲染) };
关键修改点
- 重置数据数组:用
querySnapshot.docs.map直接生成新数组,替代push累加,确保每次快照都是全新数据。 - 清理监听器:在
useEffect中返回订阅的取消函数,当组件卸载或workoutIds变化时,自动取消旧的监听,避免重复更新。 - 空值处理:增加
workoutIds为空时的处理逻辑,直接清空列表,避免不必要的查询。
内容的提问来源于stack exchange,提问作者theproductivepickle
相关产品推荐
相关产品推荐

