React中如何合并两个Firestore实时监听器的结果为对象数组
问题核心原因
forEach不支持异步回调等待,即使你在循环里加了async也不会等待所有get()请求完成,执行setManagerRequests时数组还没被填充完成- 你在
useEffect同步执行阶段就合并数组,此时两个监听器的onSnapshot异步回调还没触发,拿到的永远是初始空值 - 热重载会触发重渲染,此时之前的异步请求已经返回了state值所以能显示,刷新页面后state重置就回到空的情况
现有修复版的隐藏问题
你当前的修复方案里把managerUsername、employeeUsername等设为全局state,每个文档循环都会覆盖同一个state的值,最终所有请求里的用户名都会变成最后一个查询到的结果,存在数据错乱的隐患,不建议这么用。
优化后的实现方案
不需要单独维护多个username的state,也不需要拆分多余useEffect,优化后代码如下:
const [requests, setRequests] = useState([]); const [managerRequests, setManagerRequests] = useState([]); const [employeeRequests, setEmployeeRequests] = useState([]); const [loading, setLoading] = useState(true); // 监听manager请求 useEffect(() => { if (!firebase) return; const unsubscribe = firebase.getManagerRequests({ userUid: user.uid, onSnapshot: async (snapshot) => { // 用map生成所有文档的处理promise,再用Promise.all等待全部完成 const reqPromises = snapshot.docs.map(async (doc) => { const [managerRes, employeeRes] = await Promise.all([ doc.data().managerUser.get(), doc.data().employeeUser.get() ]); return { id: doc.id, managerUsername: managerRes.data().username, employeeUsername: employeeRes.data().username, ...doc.data() }; }); const managerReqs = await Promise.all(reqPromises); setManagerRequests(managerReqs); setLoading(false); } }); return () => unsubscribe?.(); }, [firebase, user]); // 监听employee请求 useEffect(() => { if (!firebase) return; const unsubscribe = firebase.getEmployeeRequests({ userUid: user.uid, onSnapshot: async (snapshot) => { const reqPromises = snapshot.docs.map(async (doc) => { const [managerRes, employeeRes] = await Promise.all([ doc.data().managerUser.get(), doc.data().employeeUser.get() ]); return { id: doc.id, managerUsername: managerRes.data().username, employeeUsername: employeeRes.data().username, ...doc.data() }; }); const employeeReqs = await Promise.all(reqPromises); setEmployeeRequests(employeeReqs); setLoading(false); } }); return () => unsubscribe?.(); }, [firebase, user]); // 专门监听两个数组的变化,自动合并 useEffect(() => { setRequests([...managerRequests, ...employeeRequests]); }, [managerRequests, employeeRequests]); // 渲染时可加loading状态避免空数据 return ( <> <Navbar /> {loading ? <div>加载中...</div> : <Dashboard requests={requests} />} <Footer /> </> )
优化点说明
- 用
docs.map配合Promise.all等待所有文档的用户信息查询完成,保证赋值给state的时候数组已经是完整数据 - 单独抽离合并逻辑的useEffect,依赖项设为两个请求数组,只要任意一个数组更新就自动合并到requests里,原生支持Firestore实时更新
- 去掉冗余的username state,避免不必要的重渲染和依赖项无限触发的问题
内容的提问来源于stack exchange,提问作者uponly
相关产品推荐
相关产品推荐

