Search code examples
javascriptreactjsarraysforeachnested

pushing objects into an array acts differently in a nested forEach - React JS


I need some help to understand why this is acting this way to learn better:

I have a simple funciton to fetch data from database, but when I try to push the data in the second foreach loop, I am unable to render the items by using array.map as if objects were pushed in the first forEach.

Here is the code and I will attach some screen shots of the outcome I expect.

    const fetchPrices = async () => {
        try {
            let data=[]
            const products = query(collection(db, 'products'), where("active", "==", true));
            const querySnapshot = await getDocs(products);
            querySnapshot.forEach(async (doc) => {
                //works fine if I do it in this forEach
                //let obj = {planId: doc.id, planName: doc.data().name}
                const priceSnap = await getDocs(collection(doc.ref, 'prices'));
                priceSnap.forEach( (price) => {
                    //this is where the challenge comes from...
                    let obj = {planId: doc.id, planName: doc.data().name, priceId: price.id, priceAmount:price.data().unit_amount}
                    data.push(obj); 
                });
                //data.push(obj);
            });
    
            console.log(data);
            let listitems = data.map((d) => <MenuItem key={d.planId} value={d.priceId}>{d.planName}</MenuItem>)
            setSubscriptionPlans(listitems); 
            
        } catch (err) {
          console.error(err);
        }
      };

what I expect to get: (what I get when I push the obj in the first loop)

expexted

what I get when pushing the obj from the second one:

broken

Please feel free to let me know if you need more info.

Thanks,


Solution

  • Your problem is here

            priceSnap.forEach( (price) => {
                    //this is where the challenge comes from...
                    let obj = {planId: doc.id, planName: doc.data().name, priceId: price.id, priceAmount:price.data().unit_amount}
                    data.push(obj); 
                });
     console.log(data);
    

    This inner forEach would handle a bunch of promises and will not actually force the outer block of code to wait until it finishes. It will fire the forEach and continue outside of this block of code to the outer console.log(data) while the loop still executes.

    Try with the following code instead.

      const fetchPrices = async () => {
            try {
                let data=[]
                const products = query(collection(db, 'products'), where("active", "==", true));
                const querySnapshot = await getDocs(products);
    
                for await (const doc of querySnapshot) {
                   for await (const price of getDocs(collection(doc.ref, 'prices')) ) {
                     let obj = {planId: doc.id, planName: doc.data().name, priceId: price.id, priceAmount:price.data().unit_amount}
                     data.push(obj);
                   }
                }
    
           console.log(data);
           let listitems = data.map((d) => <MenuItem key={d.planId} value={d.priceId}>{d.planName}</MenuItem>)
           setSubscriptionPlans(listitems); 
            
           } catch (err) {
          console.error(err);
        }
      };