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

React中如何等待forEach循环内的异步Firestore请求完成再更新状态

问题:Firestore异步请求导致初始加载时followersData为空

我在React组件中尝试从Firestore获取关注者数据时遇到了问题:初始页面加载时,控制台打印的followersData为空;但重新渲染组件后,就能成功获取到完整数据。

以下是我当前的代码实现:

function Followers({ urlname, navigation }) {
  const [followersData, setfollowersData] = useState(null)
  useEffect(() => {
    // Fetch Followers data
    const dataRef = firestore().collection('usernames')
    dataRef.doc(urlname)
      .collection('Following')
      .get()
      .then((docSnapshot) => {
        let items = []
        docSnapshot.forEach(doc => {
          let item = {}
          // Store followers, display name and follows info
          dataRef
            .doc(doc.id)
            .get()
            .then((followerDoc) => {
              const data = followerDoc.data();
              item.profileName = doc.id
              item.displayName = data.userName
              item.followerCount = data.followers
              item.followingCount = data.following
            })
          items.push(item)
        })
        setfollowersData(items)
      })
  }, [])
  return (
    <>
      {console.log('followersData', followersData)}
    </>
  )
}
export default Followers

初始页面加载时的控制台输出:

followersData null
followersData [{}, {}, {}, {}]
followersData [{}, {}, {}, {}]

组件重新渲染后的控制台输出:

followersData [包含4个完整数据对象的数组]

请问如何修改代码,确保等待forEach循环内的所有异步Firestore请求完成后,再更新followersData状态?


解决方案

这个问题的核心是你在异步请求完成前就更新了状态。forEach循环里的dataRef.doc(doc.id).get()是异步操作,当你把空的itempush到items数组并调用setfollowersData时,这些异步请求还没返回数据,所以初始状态里的数组都是空对象。后续当异步请求完成时,虽然修改了item的属性,但这时候状态已经更新过了,只有当组件因为其他原因重新渲染时,才会看到填充好的数据——这显然不是我们想要的同步逻辑。

要解决这个问题,我们需要用Promise.all来等待所有内部的异步请求完成,再统一更新状态,具体修改如下:

function Followers({ urlname, navigation }) {
  const [followersData, setfollowersData] = useState(null)
  useEffect(() => {
    // Fetch Followers data
    const dataRef = firestore().collection('usernames')
    
    dataRef.doc(urlname)
      .collection('Following')
      .get()
      .then(async (docSnapshot) => {
        // 用map收集每个followers的异步请求Promise
        const followerPromises = docSnapshot.docs.map(async (doc) => {
          const followerDoc = await dataRef.doc(doc.id).get();
          const data = followerDoc.data();
          // 直接返回组装好的对象
          return {
            profileName: doc.id,
            displayName: data.userName,
            followerCount: data.followers,
            followingCount: data.following
          };
        });
        
        // 等待所有Promise完成,拿到完整的followers数据数组
        const items = await Promise.all(followerPromises);
        // 最后统一更新状态
        setfollowersData(items);
      })
      .catch(err => {
        // 别忘了添加错误处理
        console.error('获取关注者数据失败:', err);
      });
  }, [urlname]) // 这里要把urlname加入依赖数组,确保urlname变化时重新请求数据
  return (
    <>
      {console.log('followersData', followersData)}
    </>
  )
}
export default Followers

关键修改点说明:

  1. 用map替代forEach收集Promise:docSnapshot.docs.map会返回一个包含所有异步请求Promise的数组,每个Promise最终会解析为一个组装好的关注者对象。
  2. 用Promise.all等待所有异步请求:await Promise.all(followerPromises)会等待数组里的所有Promise都完成,然后返回一个包含所有结果的数组,这时候我们才拿到了完整的关注者数据。
  3. 添加urlname到依赖数组:你的原始代码里useEffect的依赖是空数组,这意味着只会在组件挂载时执行一次。如果urlname变化,组件不会重新请求数据,把urlname加入依赖可以避免这个问题。
  4. 添加错误处理:在异步流程中添加catch可以捕获Firestore请求可能出现的错误,方便调试和用户提示。

这样修改后,初始加载时就会等待所有异步请求完成,再更新followersData状态,控制台会直接打印出包含完整数据的数组,不会再出现空对象的情况。

内容的提问来源于stack exchange,提问作者Junaid Razaq

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 04:38:10