Home > Mobile >  Using getDoc().then() inside of a loop Firebase
Using getDoc().then() inside of a loop Firebase

Time:12-05

I am trying to loop through an array and for each element in an array i want to use getDoc() and then add the data of the document to a new array. Basically to do this:

useEffect(() => {
    let items = [];
    for(const cart_item of cartItems){
        getDoc(doc(getFirestore(), 'tickets', cart_item.ticket_id)).then((ticket_item) => {
            items.push({...ticket_item.data()});
        });
    }
    console.log(items);
}, [cartItems])

However, the items array is still empty after a loop where i log it. I think that the issue is that the loop is not waiting for the getDoc() to return something and goes on. How to make it wait for the getDoc() to finish? I have been reading about async, await but i still don't understand how to handle it.

CodePudding user response:

Try refactoring your code with an async function as shown below:

useEffect(() => {
  const getItems = async () => {
    // fetching all documents by mapping an array of promises and using Promise.all()
    const itemsDocs = await Promise.all(cartItems.map(c => getDoc(doc(getFirestore(), 'tickets', c.ticket_id)))
    // mapping array of document data
    const items = itemsDocs.map(i => i.data())
    console.log(items);
  }
  getItems();
}, [cartItems])

CodePudding user response:

When you call getDoc it needs to make a call to the Firestore server to get your data, which may take some time. That's why it doesn't immediately return the document snapshot, but instead you use a then() callback that the Firestore SDK calls when it gets the data back from the server.


This sort of asynchronous call changes the order in which you code executes, which is easiest to see if we add some logging to the code:

console.log("Before loading data");
for(const cart_item of cartItems){
    getDoc(doc(getFirestore(), 'tickets', cart_item.ticket_id)).then((ticket_item) => {
        console.log("Got data");
    });
}
console.log("After starting data load");

Then you run this code, the logging output i:

Before loading data

After starting data load

Got data

Got data

...

This is probably not what you initially expected, but it perfectly explains why your console.log(items) shows an empty array: by that time none of the calls to items.push({...ticket_item.data()}) have run yet, because the data is still being loaded from the server.


I noticed that Dharmaraj just posted an answer with the code for a working solution using Promise.all, so I recommend checking that out too.

  • Related