Why this works
const handleToggle = (id) => {
const newTodos = [...todos]
newTodos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
});
setTodos(newTodos);
}
And this doesnt
const handleToggle = (id) => {
setTodos(prevTodos => prevTodos.map(todo => {
if (todo.id === id) {
todo.completed = !todo.completed
}
}))
}
Why do i have to create a copy of the old todos array if i want to change some item inside it?
>Solution :
You are doing a copy of the array in both cases, and in both cases you are mutating the state directly which should be avoided. In the second version you also forgot to actually return the todo, so you will get an array of undefined.
Instead you should shallow copy the todo you want to update.
const handleToggle = (id) => {
setTodos(prevTodos => prevTodos.map(todo => {
if (todo.id === id) {
return {...todo, completed: !todo.completed}
}
return todo
}))
}
Why mutating state is not recommanded? source
Debugging: If you use console.log and don’t mutate state, your past
logs won’t get clobbered by the more recent state changes. So you can
clearly see how state has changed between renders.Optimizations: Common React optimization strategies rely on skipping
work if previous props or state are the same as the next ones. If you
never mutate state, it is very fast to check whether there were any
changes. If prevObj === obj, you can be sure that nothing could have
changed inside of it.New Features: The new React features we’re
building rely on state being treated like a snapshot. If you’re
mutating past versions of state, that may prevent you from using the
new features.Requirement Changes: Some application features, like implementing
Undo/Redo, showing a history of changes, or letting the user reset a
form to earlier values, are easier to do when nothing is mutated. This
is because you can keep past copies of state in memory, and reuse them
when appropriate. If you start with a mutative approach, features like
this can be difficult to add later on.Simpler Implementation: Because
React does not rely on mutation, it does not need to do anything
special with your objects. It does not need to hijack their
properties, always wrap them into Proxies, or do other work at
initialization as many “reactive” solutions do. This is also why React
lets you put any object into state—no matter how large—without
additional performance or correctness pitfalls.In practice, you can often “get away” with mutating state in React,
but we strongly advise you not to do that so that you can use new
React features developed with this approach in mind. Future
contributors and perhaps even your future self will thank you!