How to wait promises ad push data to array in forEach loop [react]

六眼飞鱼酱① 提交于 2021-01-28 18:35:00

问题


So i want to use this effect:

useEffect(() => {
    return db.collection('users').orderBy('lastName').onSnapshot(snap => {
       const usersArray = []
       snap.forEach( async doc => {
         const user = doc.data()
         if(user.manager) {
            await Promise.all([
            db.collection('users').doc(user.manager).get(),
            db.collection('roles').doc(user.profile).get()
          ]).then(resp => {
                    const manager = resp[0].data()
                    const role = resp[1].data()
                    usersArray.push({
                      id: doc.id,
                      ...user,
                      manager: `${manager.lastName} ${manager.name} ${manager.middleName}`,
                      role: role.profile
                    })
                  })
         } else {
           await db
             .collection('roles')
             .doc(user.profile)
             .get().then(resp => {
              const role = resp.data()
              usersArray.push({
                id: doc.id,
                ...user,
                role: role.profile
              })
             })
         }
         usersArray.push({test: 'test'})
       })
       console.log(usersArray)
       setAgents(usersArray)
     })
   }, [])

Here i'm getting list of users, then i want to get for each user data about their role in system and their managers, and update user object and push to array. After that i want to render it. But now, i'm always getting empty array. In console.log of .then blocks data is correct.

Please help to get right code


回答1:


That is because the console.log part of the code is synchronously executed before any of the data is actually written into the array. forEach array property creates a non blocking callback independent of the main execution. If you want to make sure that every item in snap is processed before executing the console.log I recommend using map instead, map has a unique property of actually returning a value (as opposed to foreach). In this case that return value would be callback promise. You could just await the result of the map to make sure all items were written into the array or even better return the actual items inside of the callback.

here is example code (for simplicity i removed the usersArray.push({test: 'test'}) which i assume is unneeded)

useEffect(() => {
    return db.collection('users').orderBy('lastName').onSnapshot(snap => {
        const usersArrayPromise = snap.documents.map(doc => {
            const user = doc.data();

            if (user.manager) return Promise.all([
                db.collection('users').doc(user.manager).get(),
                db.collection('roles').doc(user.profile).get()
            ]).then(resp => {
                const manager = resp[0].data();
                const role = resp[1].data();
                return {id: doc.id, ...user, manager: `${manager.lastName} ${manager.name} ${manager.middleName}`, role: role.profile};
            });

            return db.collection('roles')
                .doc(user.profile)
                .get().then(resp => {
                    const role = resp.data();
                    return {id: doc.id, ...user, role: role.profile}
                })
        });
        Promise.all(usersArrayPromise).then(usersArray => {
            console.log(usersArray)
            setAgents(usersArray)
        });
    })
}, []);

Please take a note of the fact that firebase QuerySnapshot isn't an actual array but implements array like forEach property, if you want to use map you would do snap.documents.map.



来源:https://stackoverflow.com/questions/58483486/how-to-wait-promises-ad-push-data-to-array-in-foreach-loop-react

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!