[英]useState not updating an array correctly
I'm using React with Firebase to fetch data from firestore and display it to the user.我正在使用 React 和 Firebase 从 firestore 获取数据并将其显示给用户。 Within a useEffect hook, I fetch the data and add it to an array of json objects.在 useEffect 挂钩中,我获取数据并将其添加到 json 个对象的数组中。 However, when adding a new element to the array, the previous one gets deleted.但是,当向数组添加新元素时,前一个元素将被删除。 Below will be relevant portions of code.下面将是代码的相关部分。
const [items, setItems] = useState([]);
const [isLoading, setLoading] = useState(true);
const { currentUser } = useAuth();
function addItemToList(i) {
const updatedList = [
...items,
{
data: i.data(),
ref: i.ref
}
]
return updatedList;
}
useEffect(() => {
firestore.collection('items').where('uid', '==', currentUser.uid).get().then((itemSnapshot) => {
itemSnapshot.forEach((doc) => {
setItems(addItemToList(doc));
})
})
setLoading(false);
}, []);
I've also tried updating the items
list within the useEffect
hook, but it produces the same bug.我还尝试更新useEffect
挂钩中的items
列表,但它会产生相同的错误。
You need to add "addItemToList" to the dependencies of the useEffect hook.您需要将“addItemToList”添加到 useEffect 挂钩的依赖项中。
Because you haven't done this, the effect references an old version of that function which references an old version of the items array.因为您还没有这样做,所以效果引用了旧版本的 function,它引用了旧版本的 items 数组。
You just use itemSnapshot.map
like this:您只需像这样使用itemSnapshot.map
:
firestore.collection('items').where('uid', '==', currentUser.uid).get().then((itemSnapshot) => {
setItems(
itemSnapshot.map((doc) => ({
data: i.data(),
ref: i.ref,
})),
);
})
Let me take a moment to explain what is happening here.让我花点时间解释一下这里发生的事情。
Your have to think in your react component as a "Snapshot", where a snapshot is the result of a render.您必须将反应组件视为“快照”,其中快照是渲染的结果。 Each snapshot points to a state created by useState and only to that one.每个快照都指向由 useState 创建的 state,并且仅指向该快照。 What does this mean?这是什么意思?
The first time your component renders (first snapshot) the items variable returned by useState is pointing to an empty array and as long as that snapshot is alive it would be pointing to that array.组件第一次呈现(第一个快照)时,useState 返回的 items 变量指向一个空数组,只要该快照存在,它就会指向该数组。
So, lets take a look to the function called by useEffect那么,让我们看一下 useEffect 调用的 function
firestore.collection('items').where('uid', '==', currentUser.uid).get().then((itemSnapshot) => {
itemSnapshot.forEach((doc) => {
setItems(addItemToList(doc));
})
})
setLoading(false);
you are calling the set function for items once per elemtent in firebase query result and each call is seting a value that is the result of calling addItemToList您正在为 firebase 查询结果中的每个元素调用一次集合 function,并且每次调用都设置一个值,该值是调用 addItemToList 的结果
function addItemToList(i) {
const updatedList = [
...items,
{
data: i.data(),
ref: i.ref
}
]
return updatedList;
}
the point here is that you are always destructuring...items which is always the items variable that the snapshot of the component is pointing to and it would not be updated until the component re rederds.这里的要点是你总是在解构...items,它总是组件快照指向的 items 变量,并且在组件重新更新之前不会更新。
So, basically you all aways doing updatedList = [...[], {... } ] becauses items has to wait to take update his value.所以,基本上你们都在做 updatedList = [...[], {... } ] 因为项目必须等待更新他的价值。
@Viet already responded with something good, which I think it's the best option @Viet 已经回复了一些好东西,我认为这是最好的选择
mapping the result of the query to an array and updating the array with the result of the map a parameter.将查询结果映射到数组,并使用 map 参数的结果更新数组。
useEffect(() => {
firestore.collection('items').where('uid', '==', currentUser.uid).get().then((itemSnapshot) => {
const result = [];
itemSnapshot.forEach((doc) => {
result.push({
data: i.data(),
ref: i.ref
});
})
setItems(result);
})
setLoading(false);
}, []);
Instead of running it for each value maybe you should try to map it as a list and update the state once与其为每个值运行它,不如尝试将 map 作为列表并更新 state 一次
function addItemsToList(newItems) {
const updatedList = [
...items,
...newItems
]
return updatedList;
}
useEffect(() => {
firestore.collection('items').where('uid', '==', currentUser.uid).get().then((itemSnapshot) => {
const docs = itemSnapshot.map((doc) => ({
data: doc.data(),
ref: doc.ref
});
setItems(addItemsToList(docs));
})
setLoading(false);
}, []);
You are using spread operator in a wrong way!您以错误的方式使用传播运算符! This way will not update the array.这种方式不会更新数组。
You can do that by using es6 spread to concat multiple arrays something like below:您可以通过使用es6 spread 连接多个 arrays 来做到这一点,如下所示:
const updatedList = [
...items,
...[{ data: i.data(), ref: i.ref }]
]
To know more: Using es6 spread to concat multiple arrays了解更多: 使用 es6 spread to concat multiple arrays
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.