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
关键修改点说明:
- 用
map替代forEach收集Promise:docSnapshot.docs.map会返回一个包含所有异步请求Promise的数组,每个Promise最终会解析为一个组装好的关注者对象。 - 用
Promise.all等待所有异步请求:await Promise.all(followerPromises)会等待数组里的所有Promise都完成,然后返回一个包含所有结果的数组,这时候我们才拿到了完整的关注者数据。 - 添加
urlname到依赖数组:你的原始代码里useEffect的依赖是空数组,这意味着只会在组件挂载时执行一次。如果urlname变化,组件不会重新请求数据,把urlname加入依赖可以避免这个问题。 - 添加错误处理:在异步流程中添加
catch可以捕获Firestore请求可能出现的错误,方便调试和用户提示。
这样修改后,初始加载时就会等待所有异步请求完成,再更新followersData状态,控制台会直接打印出包含完整数据的数组,不会再出现空对象的情况。
内容的提问来源于stack exchange,提问作者Junaid Razaq
相关产品推荐
相关产品推荐

