React中useState对象数组为何重复添加相同message数据?
问题:React + Firebase 中message字段重复赋值的解决方法
你在开发React应用时,从Firebase数据库获取数据并存入useState对象数组,目前其他数据字段都能正常保存,但每个对象的message字段都被设置成了相同的值。
原代码
useEffect(() => { const RecentUsers = () => { const usersRef = ref(dbref, 'RecentChatWeb/' + curUser.uid); onValue(usersRef, (snapshot) => { // array for storing the keys const recentUserIds = []; // Getting the keys and snapshot.forEach((doc) => { const recentkey = doc.key; recentUserIds.push(recentkey); // HERE I AM FETCHING AND SAVING MESSAGES IN MSG VARIABLE const msg = doc.child("message").val(); const fetchData = async() => { const recentUserDocs = []; for(const userIds of recentUserIds){ const q = query(collection(dataref, "Users"), where("uid", "==", userIds)); const querySnapshot = await getDocs(q); querySnapshot.forEach((doc) => { console.log(doc) recentUserDocs.push({ id: doc.id, message : msg, recentUser: doc.data() }); }); } setRecentUsers(recentUserDocs) }; fetchData() }); }); } RecentUsers() },[curUser])
问题原因
- 闭包导致变量引用错误:
snapshot.forEach循环内定义的fetchData异步函数,会捕获循环中的msg变量。由于异步函数执行时循环已完成,所有fetchData实例都会引用循环最后一次的msg值,最终所有对象的message都相同。 - 重复执行与数据覆盖:每次循环都调用
fetchData,会多次触发setRecentUsers覆盖之前的结果,同时recentUserIds不断累加,导致数据逻辑混乱。
修正方案
先一次性收集所有用户ID和对应的message,再统一处理用户数据,避免闭包和重复执行的问题:
useEffect(() => { const fetchRecentUsers = async () => { const usersRef = ref(dbref, 'RecentChatWeb/' + curUser.uid); const snapshot = await get(usersRef); // 改用get获取单次快照,需实时监听可保留onValue,但要处理取消订阅 // 收集用户ID和对应message的映射关系 const userMessageMap = {}; const recentUserIds = []; snapshot.forEach((doc) => { const userId = doc.key; recentUserIds.push(userId); userMessageMap[userId] = doc.child("message").val(); }); const recentUserDocs = []; // 遍历用户ID,关联对应的message for (const userId of recentUserIds) { const q = query(collection(dataref, "Users"), where("uid", "==", userId)); const querySnapshot = await getDocs(q); querySnapshot.forEach((doc) => { recentUserDocs.push({ id: doc.id, message: userMessageMap[userId], // 从预存映射中取对应message recentUser: doc.data() }); }); } setRecentUsers(recentUserDocs); }; if (curUser?.uid) { // 确保curUser初始化后再执行 fetchRecentUsers(); } }, [curUser]);
关键优化点
- 用
userMessageMap关联用户ID和对应的message,彻底避免闭包导致的变量引用错误 - 将异步逻辑统一放在外层函数,避免循环内重复执行异步操作和状态更新
- 添加
curUser?.uid判断,防止未初始化时的报错 - 若需实时监听数据,可保留
onValue,但要在组件卸载时取消订阅,避免内存泄漏
内容的提问来源于stack exchange,提问作者Vivek Fauzdar
相关产品推荐
相关产品推荐

