1

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 (e.g. 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!

Alhxor
  • 29
  • 3

1 Answers1

1

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.

vatz88
  • 2,422
  • 2
  • 14
  • 25
  • Oh, I've completely missed, that they also spread foundTask! This makes sense now, thank you. So there is an error in book code - and it's just not showing up visually. – Alhxor Aug 22 '19 at 14:53
  • Yes, if it is same as you have in your question. You should run the code and check the console. – vatz88 Aug 22 '19 at 15:08
  • @Alhxor you seem to be new to this site. If you find any answer helpful or looks like it is the solution you were looking for you can upvote it or mark it as accepted. You don't need to edit your question to put the answer into it – vatz88 Aug 22 '19 at 15:13
  • Yep, first question. Don't have enough rep for upvote to register yet. Thanks again. :) – Alhxor Aug 22 '19 at 19:34