使用 useEffect() Hook 和 Async/Await 从 Firebase/Firestore 获取数据

Posted

技术标签:

【中文标题】使用 useEffect() Hook 和 Async/Await 从 Firebase/Firestore 获取数据【英文标题】:Using useEffect() Hook and Async/Await to Fetch Data from Firebase/Firestore 【发布时间】:2020-02-02 06:17:35 【问题描述】:

1。我的意图:

使用 useEffect() 钩子从 props 中初始化组件的状态变量,其数据是使用 mapStateToProps()从 firebase 中提取的>


2。问题:无法初始化组件的状态变量

这个想法是 useEffect 只会在组件挂载时运行一次。在这个初始化窗口中,我想将数据从 firebase 加载到这个组件的本地状态变量中。

running the example in this tutorial 时可以正常工作。在其中,他在 async/await 函数中使用 axios() 获取数据。有关他的代码,请参见下面的第 3 节。

但是当我这样做时,尝试从与 Firestore 同步或初始化的道具中获取数据时,我一直为 null,无法从我的异步/等待函数返回的 result .

我认为这是因为我对 async/await 缺乏了解。我无法创建有效的 async/await 函数来从道具中检索数据。

问题:

我怀疑这就是问题所在。那么,编写 async/await 函数以正确检索与 firebase 同步的道具中的数据的正确方法是什么?

我的问题代码:

    import React,  useState, useEffect from "react";
    import  connect  from "react-redux";
    import  firestoreConnect  from "react-redux-firebase";
    import  compose  from "redux";

    const app = props => 
      const  order  = props;  
      const [data, setData] = useState();

      useEffect(() => 
        const fetchData = async () => 
          const result = await (() => 
            return new Promise(resolve => 
              if (order)  resolve(order); 
            );
          )();
          setData(result);
        ;
        fetchData();
      , []);
      /** if I leave useEffect's second argument empty, which is intended to run only once at the beginning, then i'm getting null  */

    ...

    const mapStateToProps = state => 
      const id = "DKA77MC2w3g0ACEy630g"; // docId for testing purpose.
      const orders = state.firestore.data.orders;
      const order = orders ? orders[id] : null;        
      return 
        order: order
      ;
    ;

    export default compose(
      connect(mapStateToProps),
      firestoreConnect([collection: "orders"])
    )(app);

我的尝试:

如果我将 order 添加到第二个参数中,那么它将正确地将数据加载到组件的状态变量中。但它会将道具的数据重新加载到组件的状态变量中,并在每个重新渲染周期中清除所有更改。
 useEffect(() => 
        ...
      , [order]);
mapStateToProps() 是否发生在初始组件装载之后?因为这可以解释为什么在初始效果中,道具总是空的。

3。我正在学习的教程:使用钩子获取数据

我能够使用techniques from this tutorial成功获取数据:

  const [data, setData] = useState( hits: [] );
  useEffect(() => 
    const fetchData = async () => 
      const result = await axios(
        "https://hn.algolia.com/api/v1/search?query=redux"
      );
      setData(result.data);
    ;
    fetchData();
  , []);

【问题讨论】:

您的数据不是已经被firestoreConnect 获取并且在props 中可用吗?尝试将props 记录到控制台。 这不是你问题的确切答案,但我想这是你的问题之一,async/await 函数本身就是产生承诺的生成器的糖语法,所以你不需要等待为了一个承诺 【参考方案1】:

听起来order 可能是父级每次创建的对象。钩子依赖检查完全匹配 -- previous === next -- 所以即使对象内部的值没有改变,如果对象本身改变,效果会再次运行。

发生这种情况的最常见方式是传递普通的对象字面量:

const order =  id: 'foo', SKU: 'bar', cost: 19.95, quantity: 1 ;
return <App order=order />;

这将每次使用新的对象字面量重新填充order。当您将一个对象散布到另一个对象中时,会发生更隐蔽的文字重新创建:

const order =  ...oldOrder, quantity: 2 ;

这仍然会创建一个新对象。

不要试图改变现有的对象。 React 会开始对你做一些非常奇怪的事情。一旦你理解了钩子依赖是如何工作的,变异对象会让你陷入困境。

你有三个选择:

使用原语作为依赖项

如果您可以确定应该从服务器强制刷新的内容,请将其拉​​入它自己的变量中,并基于此设置您的依赖关系。仅当您只需要向服务器发送有限数量的字段时,这才有效。

如果在您的示例中,您只需要订单的 id 即可拨打电话,您可以将其提取到自己的变量中:

const orderId = order.id;

useEffect(() => 
  const fetchData = async () => 
    const response = await axios(`url-with-id?orderId=$orderId`);
    setData(response.data);
  ;
  fetchData();
, [orderID]);

请注意,如果您尝试从效果中访问整个 order 对象,即使您没有将它包含在依赖数组中,React 也会抱怨,这是有充分理由的。除非你可以绝对保证它永远不会改变,除非orderId 也会改变,否则它可能会导致运行时错误,以后会遇到你。仅在效果中使用依赖数组中的内容。

记忆你的对象

如果您的order 对象非常简单,您可以根据其内容记忆它。 useMemo 采用依赖数组,与 useEffect 一样,但不是在依赖更改时运行效果,useMemo 在依赖更改时返回一个新对象。

只要没有任何依赖关系发生变化,useMemo 就会返回相同的对象。这就是为什么它起作用的关键。

如果order 有几个简单的值,例如idSKUpricequantity,您可以将对象字面量创建更改为如下所示:

const order = useMemo(
  () => (
    id,
    SKU,
    price,
    quantity
  ),
  [id, SKU, price, quantity]
);

现在,只要order 中的数字或字符串没有变化,每次渲染都会得到相同的对象。因为它是同一个对象,useEffect(..., [order]) 只会运行一次。如果其中一个值发生变化,则将提供一个新的 order 对象,useEffect 的依赖数组发生变化,并为新的 order 运行效果。

查看为什么您不断收到新的order 对象

在您的情况下,您似乎是从 Redux 商店获取您的 order 对象。 Redux 仅在必要时才依赖其数据进行更改。每次 Redux 中的数据发生变化时,都会触发每个需要它的组件的变化。如果数据发生不必要的更改,这可能会导致您获取不断更新的对象。

您需要查看是否有另一个组件将不必要的操作分派给 Redux,或者您的 reducer 是否在不需要时意外创建了所有新对象。父母是否在不检查是否需要更新的情况下进行更新?您是否有对数据存储进行大规模更新的异步操作(thunk 或 saga)?您是否在简单地将新数据写入旧数据之前检查传入数据和现有数据之间的差异?

Redux 状态管理是一个独立的主题。使用Redux Toolkit 可能比使用自己的更容易。它负责处理需要在商店中重新创建的内容,而无需您管理不变性问题。如果 FireBase 数据随意覆盖您的数据,Redux Toolkit 可能会更容易确保仅更新调用之间更改的数据,而不是一直更改所有内容。

【讨论】:

【参考方案2】:

试试这个:

const app = props => 
        const [data, setData] = useState( results: [] );
          useEffect(() => 
            const getData = async () => 
              const result = await axios('Your URL here');
              setData(result.data);
            ;
            getData();
          , []);

    ...

【讨论】:

【参考方案3】:

试试这个:

const app = props => 
        const [data, setData] = useState( results: [] );

          const getData = async () => 
              const result = await axios('Your URL here');
              setData(result.data);
            ;

          useEffect(() => 
            getData();
          , []);

          ...

【讨论】:

以上是关于使用 useEffect() Hook 和 Async/Await 从 Firebase/Firestore 获取数据的主要内容,如果未能解决你的问题,请参考以下文章

使用 useEffect React Hook 时出现错误 400 和 200

HOOK—useState、useEffect的使用

hook函数之useEffect的使用——自定义hook函数网络请求——

如何使用 useEffect() 更改 React-Hook-Form defaultValue?

ReactJS useEffect Hook - 在组件中多次使用?

React Hook useEffect :使用 axios 和 async await .api 获取数据,调用连续相同的 api