Search code examples
reactjsfirebasereduxuse-effectasynchronous-javascript

Cant access to method and property of an array inside React useEffect


I am using Firebase, React, Redux for my project and I want to send data from firebase to redux's global store using useEffect(). The problem I am facing is that I cannot access to methods and properties of an array even though I can use console.log(array) to get its element.

Let me briefly explain my code:

I'm assigning each group of images I get from database to its respective product and then send it to the redux global store.

My firebase structure:

products --> images

products is the root collection and images is a subcollection of products

Inside my useEffect(), I have defined 3 functions which is extractImagesFromDatabase, getOtherDataAndSendGlobalStore, and SendDataToGlobalStore. The reason why I use 2 functions to retrieve data from database is that extractImagesFromDatabase merely gets data from products/productsId/images but getOtherDataAndSendGlobalStore gets data from the products collection.

As their names imply, extractImagesFromDatabase is functioned as getting images from the database, getOtherDataAndSendGlobalStore is functioned as getting other data from the database, SendDataToGlobalStore is a function which calls both functions above.

After getting "images" with the extractImagesFromDatabase function, I push them to an array "images" which later I want to use it in the getOtherDataAndSendGlobalStore function.

After getting all the datas I want from the database using getOtherDataAndSendGlobalStore, I send these data together with the "images" array to the redux global store using an action called setProducts.


  let images = [];
  
  //load products and make it app wide
  useEffect( () => {
     function extractImagesFromDatabase() {
         db.collection("products").orderBy('timestamp', 'desc').onSnapshot(dataSnapshot => {
          // extract images and push them to images for later storage into global store
          dataSnapshot.docs.map(doc => {
          db.collection("products").doc(doc.id).collection("images").onSnapshot(imagesSnapshot => {
            images.push(imagesSnapshot.docs.map(doc => doc.data().image))    
          })  
        })
      })
    }
  
    console.log(Array.isArray(images))        // Output --> true
    console.log(images)                       // Output --> []
                                              //           > 0: (6) ["https://firebasestorage.googleapis.com/v0/b/secret…=media&token=3a900297-dbfc-4582-9d23-2039d9c46d6c", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=a465979b-9981-4ec1-a2ce-1295e3af7949", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=c9b52990-cb5c-4f99-b7c1-c1fb1644ab7f", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=f23e6056-c7f2-43d0-b45b-f3b4cd212556", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=b7e95f00-a976-4627-b83c-e6f8f2d3ec0d", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=3eda6895-4fec-48e3-b8c2-e81d666672ac"]
                                              //           > 1: (6) ["https://firebasestorage.googleapis.com/v0/b/secret…=media&token=02c4a6df-e9d2-4890-ac0c-d4357c80d4d9", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=baa5ff66-b2e9-4219-ab9b-5a01eb912656", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=75418f82-d210-4a71-bf1d-fd7f4840b901", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=e656dca8-c9b1-4a94-a2d4-e93d1d35b43a", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=66308ebe-7125-4d27-a18f-46f98cc421a9", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=2ffc3372-6b02-44db-8f42-3c1c2d9fb9d9"]
                                              //      length: 2
                                              //      __proto__: Array(0)
    console.log(images.map(image => image))   // Output --> []
    console.log(images.length)                // Output --> 0

    function getOtherDataAndSendGlobalStore() {
      images.forEach(image => {
        db.collection("products").orderBy('timestamp', 'desc').onSnapshot(dataSnapshot => {
          // push products data including images to global store
          setProducts( dataSnapshot.docs.map((doc) => ({
            product: {
              id: doc.id,
              name: doc.data().name,
              price: doc.data().price,
              description: doc.data().description,
              images: image
            }
          })
          ))
        })
      })
    }
  
   async function SendDataToGlobalStore() {
      await extractImagesFromDatabase();
      getOtherDataAndSendGlobalStore()
   }

   SendDataToGlobalStore()
  }, []);

The problem is that I can't use "forEach", "map" and even "length" for the "images" array which I defined at the top. However, when I use console.log(images) to check whether it is empty, I got 2 elements inside.

Output --> []
//           > 0: (6) ["https://firebasestorage.googleapis.com/v0/b/secret…=media&token=3a900297-dbfc-4582-9d23-2039d9c46d6c", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=a465979b-9981-4ec1-a2ce-1295e3af7949", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=c9b52990-cb5c-4f99-b7c1-c1fb1644ab7f", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=f23e6056-c7f2-43d0-b45b-f3b4cd212556", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=b7e95f00-a976-4627-b83c-e6f8f2d3ec0d", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=3eda6895-4fec-48e3-b8c2-e81d666672ac"]
//           > 1: (6) ["https://firebasestorage.googleapis.com/v0/b/secret…=media&token=02c4a6df-e9d2-4890-ac0c-d4357c80d4d9", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=baa5ff66-b2e9-4219-ab9b-5a01eb912656", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=75418f82-d210-4a71-bf1d-fd7f4840b901", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=e656dca8-c9b1-4a94-a2d4-e93d1d35b43a", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=66308ebe-7125-4d27-a18f-46f98cc421a9", "https://firebasestorage.googleapis.com/v0/b/secret…=media&token=2ffc3372-6b02-44db-8f42-3c1c2d9fb9d9"]
//      length: 2
//      __proto__: Array(0)

I have stucked in this problem for few days. Would be appreciate if someone could point out my error.


Solution

  • This code has a few obvious issues:

    1. this code will be run on every render, maybe tens of times
    2. await extractImagesFromDatabase(); won't await because the function extractImagesFromDatabase is not an async function. That's a syntactic error that you compiler should warn about.

    How to fix this:

    1. Move to lead action to external function, even in external file. The useEffect should look like this:

      useEffect( () => { sendDataToGlobalStore(setProducts) }, [setProducts]);

    2. The sendDataToGlobalStore() should look something like this:

      function SendDataToGlobalStore(setProducts) {
        db
          .collection("products")
          .orderBy('timestamp', 'desc')
          .onSnapshot(dataSnapshot => {
            dataSnapshot
              .docs
              .map(doc => {
                db 
                  .collection("products")
                  .doc(doc.id)
                  .collection("images")
                  .onSnapshot(imagesSnapshot => {
                     imagesSnapshot.docs.map(doc => {
                       const image = doc.data().image
                       db
                         .collection("products")
                         .orderBy('timestamp', 'desc')
                         .onSnapshot(dataSnapshot => {
                           setProducts( dataSnapshot.docs.map((doc) => ({
                             product: {
                                id: doc.id,
                                name: doc.data().name,
                                price: doc.data().price,
                                description: doc.data().description,
                                images: image
                             }
                           })))
                        })
                     })
                  })
              }) 
           })
        })
      }
      
    3. When you get that monstrosity of a function to work, look into making it simpler by splitting into separate functions, and learning about the async..await and promises.