Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does React create a deep or shallow copy of prevState internally?

I'm just starting with programming and have recently written my first app in React, and while it does work, I'm not sure if I'm handling my internal state correctly. My question is whether the setState method creates an immutable deep copy of the "prevState" or not necessarily? To show an example:

 menuAddRecHandler = () => {
    this.setState((prevState) => {
      const updatedState = {...prevState};
      const dayInd = updatedState.menu.recipes.findIndex(item => item.dayNum === updatedState.menuInternal.currentDay);
      updatedState.menu.recipes[dayInd].recList.push({
        name: prevState.menuInternal.menuRecSelect,
        portions: prevState.menuInternal.menuNumPortInput
      });
      return updatedState;
    })
  }

In my handler I am pushing an object to a nested array of the updatedState object which was copied over from prevState by the spread operator. Now I know that the spread operator makes only a shallow copy, but is the prevState supplied internally by the setState method also shallow, and does that mean that I am actually mutating my state directly by invoking this handler? If so, how do I fix this issue? Thank you.

like image 307
Glogov Avatar asked Oct 22 '25 02:10

Glogov


2 Answers

From the docs:

The first argument is an updater function with the signature:

(state, props) => stateChange

state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props.

How can you achieve the same result without mutating?

menuAddRecHandler = () => {
  this.setState((prevState) => {
    return {
      ...prevState,
      menu: {
        ...prevState.menu,
        recipes: prevState.menu.recipes.map((item) => {
          if(item.dayNum === prevState.menuInternal.currentDay) {
            return {
              ...item,
              recList: [
                ...item.recList,
                {
                  name: prevState.menuInternal.menuRecSelect,
                  portions: prevState.menuInternal.menuNumPortInput
                }
              ]
            }
          }
          return item;
        })
      }
    }
  });
}

This is obviously very error prone, the idea is to have less deeper states. The docs for Redux offer some tips for normalizing state.

You can also look at immutable state libraries like ImmutableJS or Immer

like image 196
Agney Avatar answered Oct 24 '25 19:10

Agney


According to the docs its's not a copy at all

state is a reference to the component state at the time the change is being applied. It should not be directly mutated.

Making a deep copy is usually bad for performance, but if you have to do it, you can do something like this:

const updatedState = JSON.parse(JSON.stringify(prevState));
like image 34
Adam Jeliński Avatar answered Oct 24 '25 17:10

Adam Jeliński