…all about setState() in React

While it is technically possible to alter state by writing to this.state directly, it will not lead to the Component re-rendering with new data, and generally lead to state inconsistency.

Three Rules of Thumb When Using setState( )

React may batch multiple setState() calls into a single update for performance.

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

You should always do this kind of manipulation with a functional approach, supplying the state and props and returning the new state based on the former.

When you call setState(), React merges the object you provide into the current state.

In the example below, we’re updating the variable dogNeedsVaccination independently of the other state variables.

The merging is shallow, so this.setState({ dogNeedsVaccination: true }) leaves the other variables intact, replacing only the value of dogNeedsVaccination.

setState is asynchronous (*)

This is why you don’t see the new values in state right after you updated it

// assuming this.state = { value: 0 }
this.setState({
value: 1
});console.log(this.state.value); // 0

React will also try to group or batch setState calls into a single call, which leads us to our first “gotcha”:

// assuming this.state = { value: 0 };
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});
this.setState({ value: this.state.value + 1});

After all the above calls are processed this.state.value will be 1, not 3 like we would expect! To get around that …

setState accepts a function as its parameter

If you pass a function as the first argument of setState, React will call it with the at-call-time-current state and expect you to return an Object to merge into state. So updating our example above to:

// assuming this.state = { value: 0 };
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));
this.setState((state) => ({ value: state.value + 1}));

Will give us this.state.value = 3 like we expected in the first place. Remember to always use this syntax when updating state to a value, which is computed based on previous state!

Common errors

One of the most common errors when using Component state is setting its value based on props in constructor. Consider following code:

class Component extends React.Component {
constructor(props) {
super(props);
this.state = { value: this.props.value };
}

render() {
return <div>The value is: {this.state.value}</div>
}
}

If the parent renders it as:

<Component value={42} />

it will correctly render “The value is 42”. But if then the parent changes to:

<Component value={13} />

it will still think that this.state.value is 42 — that’s because React will not destroy Component and recreate it — it will reuse the once rendered component and will not re-run the constructor. To get around this problem you should not assign the props to state rather use this.props.value in the render method. If you do however decide to use the state (for example because the value from props is used in a very complex computation that you don’t want to run on every render), you should implement a solution which will update the state when needed, for example:

class Component extends React.Component {
constructor(props) {
super(props);
this.state = { value: this.props.value };
} componentWillReceiveProps(nextProps) {
if(nextProps.value !== this.props.value) {
this.setState({value: nextProps.value});
}
} render() {
return <div>The value is: {this.state.value}</div>
}
}

Rememeber that any componentWill* function is not a place to trigger side effect (such as making an AJAX call), so please use componentDidUpdate(previousProps, previousState) for those, also providing similar “guard” if as above as to not run the code when no change took place.

Experience with Front-end Technologies and MERN / MEAN Stack. Working on all Major UI Frameworks like React, Angular.