使用 useEffect() 钩子从props初始化组件的状态变量,这些props的数据是使用 mapStateToProps()从firebase中提取的> 。
这个想法是,当组件安装时,useEffect将只运行一次。在此初始化窗口中,我想将数据从Firebase加载到此组件的本地状态变量。
在running the example in this tutorial时工作正常。在其中,他通过 async / await 函数中的axios()获取数据。有关其代码,请参见下面的第3节。
但是,当我这样做时,尝试从与Firestore同步或初始化的道具中获取数据时,我一直获取null,无法从我的async /返回的result
中检索数据等待功能。
我认为这是因为我缺乏对异步/等待的理解。我无法创建有效的 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);
useEffect(() => {
...
}, [order]);
我能够使用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();
}, []);
答案 0 :(得分: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
有几个简单的值,例如 id
、SKU
、price
和 quantity
,您可以将对象字面量创建更改为看起来像这样:
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 或 sagas)?在简单地将新数据写入旧数据之前,您是否检查传入数据和现有数据之间的差异?
Redux 状态管理是一个完整的主题。使用 Redux Toolkit 可能比自己动手更容易。它会处理需要在商店中重新创建的内容,而您无需管理不变性问题。如果 FireBase 数据随意覆盖您的数据,Redux Toolkit 可以更轻松地确保只更新在调用之间更改的数据,而不是一直更改所有内容。
答案 1 :(得分:0)
试试这个:
const app = props => {
const [data, setData] = useState({ results: [] });
useEffect(() => {
const getData = async () => {
const result = await axios('Your URL here');
setData(result.data);
};
getData();
}, []);
...
答案 2 :(得分:0)
试试这个:
const app = props => {
const [data, setData] = useState({ results: [] });
const getData = async () => {
const result = await axios('Your URL here');
setData(result.data);
};
useEffect(() => {
getData();
}, []);
...
}