RTK Query 偶现从Cloud Firestore获取旧数据问题求助
问题:Firestore更新后RTK Query获取旧数据
场景与现象
- 开发基于React的SPA,使用带自动重取功能的RTK Query,后端为Cloud Firestore
- 用户更新个人资料后,页面仍显示旧数据,刷新后恢复正常;问题偶发,管理员添加新产品等功能也出现过同样情况
Redux DevTools观察到的流程
- 用户执行修改操作
- RTK Query触发
updateUserCollectionmutation,进入pending状态 - mutation完成后,自动触发
getUserDataquery,进入pending状态 getUserData执行完成,但返回的是旧数据
核心问题分析
你的代码里有个关键的异步处理错误:在updateUserCollection的queryFn中,使用querySnapshot.forEach(async (doc) => { ... }),但forEach不会等待内部的异步updateDoc操作完成。这就导致mutation在Firestore的更新还没真正完成时就返回了,RTK Query立刻触发getUserData重取,此时Firestore的更新可能还没同步到读取节点,自然拿到旧数据。
解决方案
1. 修复mutation的异步等待逻辑
把forEach换成Promise.all,确保所有updateDoc操作完成后,mutation才返回:
updateUserCollection: builder.mutation({ async queryFn(data) { try { const userQuery = query( collection(db, "users"), where("userId", "==", data[0].userId) ); const querySnapshot = await getDocs(userQuery); // 用Promise.all等待所有更新操作完成 await Promise.all( querySnapshot.docs.map(async (doc) => { await updateDoc(doc.ref, { userCollection: [...data], }); }) ); return { data: "edited" }; } catch (err) { return { error: err }; } }, invalidatesTags: ["Users", "UserData"], }),
2. 优化查询原子性(可选)
从逻辑看每个用户应该只有一条文档,建议直接通过文档ID更新,避免先查后更的额外步骤,减少竞态:
- 用户创建时,直接用
userId作为文档ID,或者在用户文档里存储docId - 更新时直接用
doc(db, "users", 目标文档ID)操作,省去getDocs查询步骤
3. 验证错误情况
要排查是否有查询失败的情况,可在getUserData的错误分支加日志,或在组件中监听错误状态:
// 在getUserData的queryFn中添加错误日志 catch (err) { console.error("获取用户数据失败:", err); return { error: err }; }
组件中使用时:
const { data, error, isError } = useGetUserDataQuery(uid); if (isError) { console.error("查询错误:", error); }
4. 直接更新缓存避免重取(可选)
如果mutation可以拿到更新后的完整数据,可直接用updateQueryData更新RTK Query缓存,不用触发重取,提升性能:
updateUserCollection: builder.mutation({ async queryFn(data) { try { const userQuery = query( collection(db, "users"), where("userId", "==", data[0].userId) ); const querySnapshot = await getDocs(userQuery); await Promise.all( querySnapshot.docs.map(async (doc) => { await updateDoc(doc.ref, { userCollection: [...data], }); }) ); // 更新完成后获取最新数据返回 const updatedSnapshot = await getDocs(userQuery); const updatedUserData = updatedSnapshot.docs[0].data(); return { data: updatedUserData }; } catch (err) { return { error: err }; } }, invalidatesTags: ["Users", "UserData"], // 乐观更新:先更新缓存,失败则回滚 async onQueryStarted(data, { dispatch, queryFulfilled }) { const patchResult = dispatch( api.util.updateQueryData('getUserData', data[0].userId, (draft) => { draft.userCollection = [...data]; }) ); try { await queryFulfilled; } catch { patchResult.undo(); } }, }),
内容的提问来源于stack exchange,提问作者Navih
相关产品推荐
相关产品推荐

