问题
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