this.state
JS-0444The only good place to assign this.state
is in an ES6 class
component constructor.
It is not recommended to mutate this.state
directly, as calling setState()
afterward may replace the mutation. You should treat this.state
as if it were immutable.
Problems with state mutations in react
React docs mention to never change this.state
directly; instead, always use this.setState
for any state updates. These are two main reasons to do this:
setState
works in batches, which means one cannot expect the setState
to do the state update immediately; it is an asynchronous operation, so the state changes may happen at a later point in time, which means manually mutating state may get overridden by setState
.
It can affect the performance. When using pure component or shouldComponentUpdate
, they will do a shallow comparison using the ===
operator. However, if one mutates the state, the object reference will still be the same, so the comparison would fail.
Recommended Approach
Suppose, we have a function updateState()
which has a mutation problem. Here are the different approaches to fix this:
Problematic code:
updateState(event) {
const {name, value} = event.target;
let user = this.state.user; // this is a reference, not a copy...
user[name] = value; // so this mutates state ?
return this.setState({user});
}
Object.assign
Object.assign
creates a copy of an object. The first parameter is the target, then specify one or more parameters for properties you’d like to tack on.
Fixed code:
updateState(event) {
const {name, value} = event.target;
let user = Object.assign({}, this.state.user);
user[name] = value;
return this.setState({user});
}
(...)
You can assume spread operator as a shorthand syntax for Object.assign()
but it overrides the value of the same key which comes first by the value that comes later.
Fixed code:
updateState(event) {
const {name, value} = event.target;
let user = {...this.state.user, [name]: value};
this.setState({user});
}
Problems to look out for
Ther's also a case when your react application state has nested objects e.g.,
let user = {
profile:{
address:{
city: ‘London’
}
}
}
If there's a need to modify city from London
to Newyork
immutably, it could be done like this:
{
...state,
user:{
...state.user,
profile:{
...state.user.profile,
address:{
...state.user.profile.address,
city:’Newyork’
}
}
}
}
So, it is recommended to keep react state as flat as possible, also consider using Immutable.js
or immutability-helper
var Hello = createReactClass({
componentDidMount: function() {
this.state.name = this.props.name.toUpperCase();
},
render: function() {
return <div>Hello {this.state.name}</div>;
}
});
class Hello extends React.Component {
constructor(props) {
super(props)
// Assign at instance creation time, not on a callback
doSomethingAsync(() => {
this.state = 'bad';
});
}
}
var Hello = createReactClass({
componentDidMount: function() {
this.setState({
name: this.props.name.toUpperCase();
});
},
render: function() {
return <div>Hello {this.state.name}</div>;
}
});
class Hello extends React.Component {
constructor(props) {
super(props)
this.state = {
foo: 'bar',
}
}
}