How to update nested states in React, should I indicate that it is immutable?

We have a heated discussion on how to update a nested state in React. Should the state be unchanged or not? What is the best practice for an elegant state statement?

Say you have a state structure that looks like this:

this.state = { numberOfStudents: "3", gradeLevel: "5", students : [ { id : "1234", firstName: "John", lastName: "Doe", email: " johndoe@mail.com " phoneNumer: "12345" }, { id : "56789", firstName: "Jane", lastName: "Doe", email: " janedoe@mail.com " phoneNumer: "56789" }, { id : "11111", firstName: "Joe", lastName: "Doe", email: " joedoe@mail.com " phoneNumer: "11111" } ] } 

Then we want to update the joe doe phone number. We can do this in several ways:

mutate state + forced update for rerender

 this.state.students[2].phoneNumber = "9999999"; this.forceUpdate(); 

mutate state + setState with mutated state

 this.state.students[2].phoneNumber = "9999999"; this.setState({ students: this.state.students }); 

Object.assign, this still mutates the state, since newStudents is just a new reference to the same this.state object points to

 const newStudents = Object.assign({}, this.state.students); newStudents[2].phoneNumber = "9999999" this.setState({ students: newStudents }); 

Unlimited Upgrade Advisor ( https://facebook.imtqy.com/react/docs/update.html ) + setState. This can become ugly very quickly if we have the address .street, address.city, address.zip in each student object and want to update the street.

 const newStudents = React.addons.update(this.state.students, {2: {phoneNumber: {$set:"9999999"}}}); this.setState({ students: newStudents }) 

The final line of the response document for setState states that:

Never mutate this.state directly, since calling setState () can subsequently replace the mutation you made. Relate to this. As if it is unchanging. https://facebook.imtqy.com/react/docs/react-component.html

The docs state that we should not use forceUpdate for reerender:

Usually you should try to avoid all uses of forceUpdate () and just read .props and this.state in render () from this.

Why is this what can happen if we change state and call setState later? Under what circumstances does setState () replace the mutation we created? This is a very confusing expression. Can someone please explain the possible complication of each of the scenarios that we use above to set the state.

+5
source share
1 answer

You indicate that:

"Object.assign, this still mutates the state, since newStudents is just a new reference to the same this.state object points to"

This statement is incorrect .
Object.assign mutates the state passed to its first parameter. Since you are passing an empty object literal ( {} ), you are mutating a new object literal and not this.state .


Some background:

The principle of an immutable state has connections with functional programming.

This is useful in React, because it provides a way for React to know if the state has changed at all , in one case it is useful to optimize when components need to re-render

Consider the case of a complex state with nested objects. Mutation of state values ​​would change the property values ​​inside the state, but would not change the reference to the object.

 this.state = {nestObject:{nestedObj:{nestObj:'yes'}}}; // mutate state this.state.nestObject.nestedObj.nestObj= 'no'; 

How do we know if React should redraw a component?

  • Check for deep equality? Imagine that it will look in a difficult state, that there are hundreds, even thousands of checks for updating the state ...
  • No need to check for changes, just make React re-display everything with every state change ...

Could there be an alternative to the last two approaches?


Immutable Method

By creating a new object (and therefore a new link), copying with the old state using Object.assign and mutating it, you can manipulate the state values ​​and change the link to the object.

With an immutable state approach, we can find out if the state has changed just by checking if the references to the objects are equal.

An example simplified for skeptics in the comments below:

Consider this simple example:

 this this.state = { someValue: 'test'} var newState = Object.assign({}, this.state); console.log(newState); // logs: Object {someValue: "test"] console.log(this.state); // logs: Object {someValue: "test"] // logs suggest the object are equal (in property and property value at least... console.log(this.state === this.state); // logs: true console.log(this.state === newState); // logs: false. Objects are // pass-by-reference, the values stored // stored in this.state AND newState // are references. The strict equality // shows that these references // DON'T MATCH so we can see // that an intent to modify // state has been made 
+2
source

Source: https://habr.com/ru/post/1263944/


All Articles