reactjs useState未正确更新数组

piok6c0g  于 2023-01-04  发布在  React
关注(0)|答案(5)|浏览(262)

我正在使用React with Firebase从firestore获取数据并显示给用户。在useEffect钩子中,我获取数据并将其添加到json对象数组中。然而,当向数组添加新元素时,前一个元素会被删除。以下是相关代码部分。

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

我还尝试过更新useEffect钩子中的items列表,但它产生了相同的bug。

7xllpg7q

7xllpg7q1#

让我花点时间解释一下这里发生的事情。
你必须把react组件想象成一个“快照”,其中快照是渲染的结果。每个快照指向useState创建的一个状态,并且只指向那个状态。这意味着什么?
组件第一次呈现(第一个快照)时,useState返回的items变量指向一个空数组,并且只要该快照处于活动状态,它就会指向该数组。
那么,让我们来看看useEffect调用的函数

firestore.collection('items').where('uid', '==', currentUser.uid).get().then((itemSnapshot) => {
    itemSnapshot.forEach((doc) => {
      setItems(addItemToList(doc));
    })
  })
  setLoading(false);

您在firebase查询结果中为每个元素调用一次项的set函数,并且每次调用都设置一个值,该值是调用addItemToList的结果

function addItemToList(i) {
  const updatedList = [
    ...items,
    {
      data: i.data(),
      ref: i.ref
    }
  ]

  return updatedList;
}

这里的要点是你总是在解构...项目,它总是组件快照所指向的项目变量,并且它不会被更新,直到组件重新排序。
所以,基本上你总是做updatedList = [...[],{...} ],因为项目必须等待更新他的值。
@Viet已经回复了好东西,我觉得这是最好的选择
将所述查询的结果Map到数组,并且用所述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);
}, []);
9rygscc1

9rygscc12#

您需要将“addItemToList”添加到useEffect钩子的依赖项中。
因为您还没有这样做,所以效果引用了该函数的旧版本,该函数引用了旧版本的items数组。

uinbv5nw

uinbv5nw3#

您只需像这样使用itemSnapshot.map

firestore.collection('items').where('uid', '==', currentUser.uid).get().then((itemSnapshot) => {
    setItems(
      itemSnapshot.map((doc) => ({
        data: i.data(),
        ref: i.ref,
      })),
    );
  })
yqkkidmi

yqkkidmi4#

也许您应该尝试将其Map为列表并更新一次状态,而不是为每个值运行它

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

qcbq4gxm5#

**您使用spread运算符的方式错误!**这种方式不会更新数组。

您可以使用es6spread来连接多个数组,如下所示:

const updatedList = [
    ...items,
    ...[{ data: i.data(), ref: i.ref }]
  ]

要了解更多信息:Using es6 spread to concat multiple arrays

相关问题