Home > OS >  Why should i create a copy of the old array before changing it using useState in react?
Why should i create a copy of the old array before changing it using useState in react?

Time:09-11

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?

CodePudding user response:

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!

CodePudding user response:

when you're changing an object using a hook correctly (- in this case useState) you are causing a re-render of the component.

if you are changing the object directly through direct access to the value itself and not the setter function - the component will not re-render and it will likely cause a bug.

and the .map(function(...)=>{..}) is a supposed to return an array by the items that you are returning from the function within. since you're not returning anything in the second example - each item of the array will be undefined - hence you'll have an array of the same length and all the items within will be undefined.

these kinds of bugs will not happen if you remember how to use array functions and react hooks correctly, it's usually really small things that will make you waste hours on end, I'd really recommend reading the documentation.

  • Related