Updating state – why creating a new copy of state when calling setState?

All we need is an easy explanation of the problem, so here it is.

React docs:

Never mutate this.state directly, as calling setState() afterwards
may replace the mutation you made. Treat this.state as if it were
immutable.

That’s clear.

class App extends React.Component {
  state = {
   data: []
  } 

the following I understand

  updateState(event) {
   const {name, value} = event.target;
   let user = this.state.user; // this is a reference, not a copy...
   user[name] = value; // 
   return this.setState({user}); // so this could replace the previous mutation
  }

this following I don’t understand

  updateState(event) {
  const {name, value} = event.target;
  let user = {...this.state.user, [name]: value};
  this.setState({user});
  }

I understand (as in previous example), that I should not either only:

  1. mutate state directly without calling setState; or
  2. mutate it and then use setState afterwards.

However, why can’t I just (without direct mutation) call setState without creating a new copy of state (no spread operator/Object.assign)? What would be wrong with the following:

  getData = () => {
   axios.get("example.com") ...
    this.setState({
     data:response.data
    })
  } 

Why should it be:

  getData = () => {
   axios.get("example.com") ...
    this.setState({
     data:[...data, response.data]
    })
  } 

 render (){ 
  ...
 }  
}

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

What would be wrong with the following:

this.setState({
   data: response.data,
});

Absolutely nothing, unless you don’t want to replace the contents of this.state.data with response.data.

Why should it be:

this.setState({
   data: [...data, response.data],
});

Because with spread you are not loosing the contents of this.state.data – you are basically pushing new response into the data array.

Note: You should use callback inside setState to get access to current data from this.state.

this.setState((prevState) => ({
   data: [...prevState.data, response.data],
}));

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply