React Tips — Async and setState

John Au-Yeung
May 12 · 4 min read
Photo by Kev Kindred on Unsplash

React is the most used front end library for building modern, interactive front end web apps. It can also be used to build mobile apps.

In this article, we’ll look at the async nature of setState and how we should write our code to run multiple setState calls sequentially.

setState’s Asynchronous Nature

The setState method is the method to update the component’s internal state. It’s an asynchronous method that’s batched. This means that multiple setState calls are batched before a component is rerendered with the new state.

setState doesn’t immediately mutate the state but creates a pending state transaction. This means that accessing the state immediately after call setState can possibly return the old value.

The setState method takes up to 2 arguments. We usually pass in only one. The first argument can be an object or a callback that’s used to update the state.

The second argument is a function that’s always run after setState is run. For instance, we can pass in a callback in the 2nd argument as follows:

import React from "react";class App extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
update() {
this.setState(
({ count }) => ({
count: count + 1
}),
() => {
this.setState(({ count }) => ({
count: count + 2
}));
}
);
}
render() {
return (
<>
<button onClick={this.update.bind(this)}>Increment</button>
<p>{this.state.count}</p>
</>
);
}
}
export default App;

In the code above, we have:

update() {
this.setState(
({ count }) => ({
count: count + 1
}),
() => {
this.setState(({ count }) => ({
count: count + 2
}));
}
);
}

which calls setState once, and then calls setState again in the callback. This will ensure that one will run after the other.

Therefore, when we click the Increment, the count state will increase by 3 every time.

setState Takes an Object or Function

As we can see from the code above, setState can take a callback that returns the new state based on the previous state. This is useful for updating states that are based on the previous state.

In the code above, the new count is based on the old count , so passing in a callback is more appropriate than passing in an object since we can guarantee that the original state is the lastest when it’s passed in as the callback parameter.

It also takes a props parameter as the second parameter which has the props. For instance, we can use it as follows:

import React from "react";class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
update() {
this.setState(({ count }, { incrementVal }) => ({
count: incrementVal + count
}));
}
render() {
return (
<>
<button onClick={this.update.bind(this)}>Increment</button>
<p>{this.state.count}</p>
</>
);
}
}
export default function App() {
return <Counter incrementVal={5} />;
}

In the code above, we have the Counter component, which has the update method as we have in the previous example. But this ti,e, the setState method takes a callback which has a second parameter. It has the props object.

We can get the prop property values as we did above via the destructuring assignment syntax. Also, we can access the prop object’s property like any other JavaScript object property with the dot or bracket notation.

Therefore, since we have:

<Counter incrementVal={5} />

in App , when we click the Increment button, the count state in Counter will update by 5 since that’s what we specified.

The most common entity that we pass into setState is probably an object. We just pass in an object that has state properties that we want to change. The ones that aren’t included will stay the same.

For instance, if we write:

import React from "react";class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0, foo: "foo" };
}
update() {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<>
<button onClick={this.update.bind(this)}>Increment</button>
<p>{this.state.count}</p>
<p>{this.state.foo}</p>
</>
);
}
}
export default function App() {
return <Counter />;
}

Then this.state.foo has the value 'foo' even after this.setstate in update is run.

Therefore, when we click the Increment button, we see ‘foo’ displayed no matter how many times we click it.

Photo by Ankush Nath Sehgal on Unsplash

Conclusion

setState calls may not be always sequential. The batching sometimes may not be controlled by React. Therefore, if we want to make sure that multiple setState calls run sequentially all the time, we should run the second setState in the callback that’s passed in as the 2nd argument of setState .

Also, setState can take an object or a function with the previous state and the props objects as the 1st and 2nd parameters respectively.

Did you know that we have four publications? Show some love by giving them a follow: JavaScript in Plain English, AI in Plain English, UX in Plain English, Python in Plain English — thank you and keep learning!

Also, we’re always interested in helping to promote good content. If you have an article that you would like to submit to any of our publications, send an email to submissions@plainenglish.io with your Medium username and what you are interested in writing about and we will get back to you!

JavaScript In Plain English

New articles every day.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store