React - 更新状态(对象数组)。我需要深拷贝数组吗?

React - Updating state (array of objects). Do I need to deep copy the array?

我有以下 state:

const[images,setImages] = useState([
  {src: 'stringSRC1', selected: false},
  {src: 'stringSRC2', selected: false},
  {src: 'stringSRC3', selected: false}
]);

我正在使用以下代码对其进行更新(切换选定状态):

function handleImageClick(index) {
  props.setImages((prevState)=>{
    const aux = Array.from(prevState);
    aux[index].selected = !aux[index].selected;
    return aux;
  });
}

它按预期工作。但是我想到了一件事。

当我从 prevState 复制数组时,我正在创建一个新数组,但对象(存储为引用)将保持不变。我已经测试过,当你像那样复制数组时它们不会改变。

问题

这是一种不好的做法吗?我是否应该像创建新数组和创建全新对象那样深入复制数组?或者这样就好了?

不,它应该是这样工作的。是的,对象没有改变,但这里重要的是数组改变了。 React 在 DOM 和虚拟 DOM 之间执行浅层比较,直到事情发生变化。一旦将新数组与旧数组进行比较,就会检测到变化,数组本身的内容并不重要。

是的,如果你想避免副作用,你必须深度克隆。

在你的函数中:

function handleImageClick(index) {
  props.setImages((prevState)=>{
    const aux = Array.from(prevState);
    aux[index].selected = !aux[index].selected;
    return aux;
  });
}

当您使用 Array.from 时,您正在获取对存储对象的引用,因此您的 prevState 已修改。

看到这个:

setTest(prevState => {
      console.log("PrevState", prevState);
      const b = Array.from(prevState);
      b[0].a = b[0].a + 1;
      return b;
    });

你可以在这里测试:

https://codesandbox.io/embed/exciting-mcnulty-6spdh?fontsize=14

您将看到 prevState 具有下一个值,因此您丢失了 prevState 值。

假设您想在代码中执行此操作:

function handleImageClick(index) {
      props.setImages((prevState)=>{
        const aux = Array.from(prevState);
        aux[index].selected = !aux[index].selected;
        aux[index + 1].selected = prevState[index].selected;
        return aux;
      });
    }

aux[index + 1].selected = prevState[index].selected;坏了!!

所以你需要深拷贝数组来避免这种情况。

你可以这样做:const aux = JSON.parse(JSON.stringify(prevState));

TL;DR - 您仍然需要制作深拷贝。

official docs建议制作深拷贝:

State can hold any kind of JavaScript value, including objects. But you shouldn’t change objects that you hold in the React state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.

他们还建议让您的状态对象尽可能扁平,以便更轻松地进行深度复制,如果这不是一个选项,则使用 immer 之类的东西来为您处理 deep-copying。

If your state is deeply nested, you might want to consider flattening it. But, if you don’t want to change your state structure, you might prefer a shortcut to nested spreads. Immer is a popular library that lets you write using the convenient but mutating syntax and takes care of producing the copies for you.