简体   繁体   中英

Why is this working: updating array element in state with spread operator

When reading React Cookbook I've stumbled upon a code snippet, this function gets called when user checks a task as completed in a TODO list:

  markAsCompleted = id => {
    // Finding the task by id...
    const foundTask = this.state.items.find(task => task.id === id);

    // Updating the completed status...
    foundTask.completed = true;

    // Updating the state with the new updated task...
    this.setState({
      items: [
        ...this.state.items,
        ...foundTask
      ]
    });
  }

UPD: Somehow I've completely missed the spread operator on foundTask . So what's really happening is the state gets updated with only ...this.state.items (which was mutated), and the ...foundTask part does not go into state, since it's not a valid spread.

At first it looked like it should add a new element to the items array, instead of updating, so I went to the JS console to check:

state = { items: [{id: '0', done: false}, {id: '1', done: false}] }

upd = state.items.find(obj => obj.id === '0') // {id: "0", done: false}

upd.done = true // also updates object inside the array

state = { items: [...state.items, upd] }

/* Indeed, adds a new element:
items: Array(3)
0: {id: "0", done: true}
1: {id: "1", done: false}
2: {id: "0", done: true}
*/

So then I've downloaded the code and ran it locally. And, to my surprise, it worked! State was getting updated without any trouble, no extra elements appeared. I used React DevTools to see the live state while testing.

I searched the web, but couldn't find any examples like in the book, but with a better explanation. Usually all solutions involve using .map() to build a new array, and then replace an existing one (eg https://stackoverflow.com/a/44524507/10304479 ).

The only difference I see between the book code snippet and console test is that React is using .setState(), so maybe this helps somehow. Can anyone help to clarify, why is it working?

Thanks!

Array.find will return the first value matched in the array. Here the array consist of objects and the value returned will be the reference to the object.

const foundTask = this.state.items.find(task => task.id === id);

Here foundTask will have reference to the same object contained in the state.items . So when you modify foundTask you're modifying the same object as in state.items .

For example,

If this.state.items is [{ id: 1 }] and if you do

const foundTask = this.state.items.find(obj => obj.id === 1);
foundTask.id = 2;
console.log(this.state.items); // [{ id:2 }]

In the code,

this.setState({
  items: [
    ...this.state.items,
    ...foundTask
  ]
});

This will update the state with updated completed value for the task. ...foundTask will give you an error in the console since foundTask will be an object and you're spreading it in an array.

Here not having ...foundTask will produce same result. Perhaps not with an error.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM