无法访问 React useEffect 中数组的方法和属性

Posted

技术标签:

【中文标题】无法访问 React useEffect 中数组的方法和属性【英文标题】:Cant access to method and property of an array inside React useEffect 【发布时间】:2021-09-09 22:24:26 【问题描述】:

我在我的项目中使用 Firebase、React、Redux,我想使用 useEffect() 将数据从 firebase 发送到 redux 的全局存储。我面临的问题是我无法访问数组的方法和属性,即使我可以使用console.log(array) 来获取它的元素。

让我简要解释一下我的代码:

我将从数据库中获得的每组images 分配给其各自的product,然后将其发送到redux 全局存储。

我的火力基地结构:

products --> images

products 是根集合,imagesproducts 的子集合

在我的 useEffect() 中,我定义了 3 个函数,它们是 extractImagesFromDatabasegetOtherDataAndSendGlobalStoreSendDataToGlobalStore。我使用 2 个函数从数据库中检索数据的原因是 extractImagesFromDatabase 仅从 products/productsId/images 获取数据,而 getOtherDataAndSendGlobalStoreproducts 集合中获取数据。

正如他们的名字所暗示的, extractImagesFromDatabase的作用是从数据库中获取图片, getOtherDataAndSendGlobalStore的作用是从数据库中获取其他数据, SendDataToGlobalStore 是一个调用上述两个函数的函数。

使用extractImagesFromDatabase 函数获取“图像”后,我将它们推送到一个数组“图像”,稍后我想在getOtherDataAndSendGlobalStore 函数中使用它。

使用getOtherDataAndSendGlobalStore 从数据库中获取我想要的所有数据后,我使用名为setProducts 的操作将这些数据与“images”数组一起发送到redux 全局存储。


  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()
  , []);

问题是我不能对我在顶部定义的“images”数组使用“forEach”、“map”甚至“length”。但是,当我使用console.log(images) 来检查它是否为空时,我得到了2 个元素。

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)

我在这个问题上纠结了几天。如果有人能指出我的错误,将不胜感激。

【问题讨论】:

错误是当您在控制台记录内容时,这些函数已定义但尚未调用。而且您只能将 await 用于返回承诺的函数。 extractImagesFromDatabase 不返回承诺。 【参考方案1】:

这段代码有几个明显的问题:

    此代码将在每次渲染时运行,可能会运行数十次 await extractImagesFromDatabase(); 不会等待,因为函数 extractImagesFromDatabase 不是异步函数。这是编译器应该警告的语法错误。

如何解决这个问题:

    移动以将操作引导至外部函数,即使在外部文件中也是如此。 useEffect 应该如下所示:

    使用效果(()=> sendDataToGlobalStore(setProducts) , [setProducts]);

    sendDataToGlobalStore() 应该如下所示:

    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
                           
                         )))
                      )
                   )
                )
            ) 
         )
      )
    
    

    当你让一个怪异的函数工作时,考虑通过拆分成单独的函数来使其更简单,并了解 async..await 和 Promise。

【讨论】:

您好,感谢您的回答,我认为 await 可以用于任何功能。感谢您指出我的错误。 我能问更多吗?我希望存储在全局存储中的图像是图像链接数组,而不仅仅是图像链接。目前,上面的代码只将 1 个链接存储到 redux 存储中。 是的,您还需要重写reducer 来处理一张一张到达的图像。而不是一次全部。因为这无论如何都会发生。 docs.map 回调是异步调用的,您不能通过将内容推送到图像数组来强制它同步。为什么不将这些图像链接存储在 redux 中?

以上是关于无法访问 React useEffect 中数组的方法和属性的主要内容,如果未能解决你的问题,请参考以下文章

react之Hook的useEffect详解

React:如何通过 Firestore onSnapshot 和 useEffect 使用最新状态?

React useEffect - 在依赖数组中传递一个函数

无法在 React 中更改 useEffect 中的变量 [重复]

React 功能组件访问状态 in useEffect

React hooks exhaustive-deps 规则也需要 useEffect 数组中的函数