You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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])   

问题原因

  1. 闭包导致变量引用错误:snapshot.forEach循环内定义的fetchData异步函数,会捕获循环中的msg变量。由于异步函数执行时循环已完成,所有fetchData实例都会引用循环最后一次的msg值,最终所有对象的message都相同。
  2. 重复执行与数据覆盖:每次循环都调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:55:26