最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin0浏览0评论

I have the following state:

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

I'm updating it (toggling selected state) with this code:

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

It works as intended. But I've thought of one thing.

When I'm copying the array from prevState, I'm creating a new array, but the objects (stored as references) will remain the same. I've tested and they do not change when you copy the array like that.

QUESTION

Is this a bad practice? Should I bother to deep copy the array, as in create a new array and create brand new objects? Or this is just fine?

I have the following state:

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

I'm updating it (toggling selected state) with this code:

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

It works as intended. But I've thought of one thing.

When I'm copying the array from prevState, I'm creating a new array, but the objects (stored as references) will remain the same. I've tested and they do not change when you copy the array like that.

QUESTION

Is this a bad practice? Should I bother to deep copy the array, as in create a new array and create brand new objects? Or this is just fine?

Share Improve this question asked May 24, 2019 at 15:59 cbdevelopercbdeveloper 31.5k44 gold badges198 silver badges396 bronze badges 9
  • FYI, the answer you've currently accepted is incorrect, details in the documentation. – T.J. Crowder Commented Dec 9, 2021 at 16:56
  • 1 @T.J.Crowder Hey T.J, thanks for your ment. Why is it incorrect? Take a look at this example that I made using produce from immer. The immutable update changes the array reference and every item that has been modified, but it does not change the reference for the items that were not touched by the update. Isn't produce used frequently to perform immutable React state updates? I wouldn't go as far and say that the content of the array does not matter, but I guess you don't have to deep clone it. Do you agree? – cbdeveloper Commented Dec 10, 2021 at 7:33
  • The documentation is clear that you cannot directly modify state items. If you do, they may not re-render, including (but not limited to, I don't think) cases where ponents avoid re-rendering if their props don't change (via React.memo, PureComponent, or shouldComponentUpdate). – T.J. Crowder Commented Dec 10, 2021 at 7:36
  • I haven't used immer, but from the documentation and a quick check, it looks like produce creates the new object for you, so you're not breaking the rule there. (If I save prevState[1] to a local, then pare it with nextState[1] after the produce call, it's a different object -- which is what the docs say you need, so all good.) – T.J. Crowder Commented Dec 10, 2021 at 7:41
  • 1 React team is writing the docs for functional-based react and there is a section that briefly touches upon the same issue. beta.reactjs/learn/…. – Thomas Sebastian Commented Dec 13, 2021 at 9:55
 |  Show 4 more ments

3 Answers 3

Reset to default 2

TL;DR - You still need to make deep copies.

The official docs remend making deep copies:

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.

They also suggest making your state objects as flat as possible to make deep copying easier and if that's not an option, using something like immer to handle the deep-copying for you.

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.

No, this is how it's supposed to work. Yeap, the objects are not changing, but the important here is that the array changes. React performs shallow parations between the DOM and the virtual DOM to now when things changed. Once your new array is pared with the old one the change will be detected, the content of the array itself doesn't really matter.

Yes, you must deep clone, if you want to avoid side effects.

In your function:

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

As you are using Array.from you are getting references to the objects stored, so your prevState is modified.

See this:

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

You can test it here:

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

You will see that prevState has the next value, so you lose your prevState value.

Imagine that you want to do this in your code:

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; is broken!!

So you need to deep copy the array to avoid this kind of things.

You can do: const aux = JSON.parse(JSON.stringify(prevState));

发布评论

评论列表(0)

  1. 暂无评论