Asynchronous React — Setting State

First let me say, React is fun. I have just started dipping my toes in it but I can already see why it has swept the industry, the speed that I am developing interesting applications is fantastic and I am excited to continue learning.

One thing I have picked up is that since React is built on JavaScript, it is a single-threaded application. This means that JavaScript (and React) can only do one thing at a time. Being an optimized, single-threaded language allows it to be very efficient and run quickly, but sometimes there are actions that by their very nature take time. This code is considered blocking code, and JavaScript treats it slightly differently.

Common examples of blocking code are API fetches, saving to a database, and, in React, setting state. These pieces of code can take an indeterminate amount of time (especially fetch as we don’t have any idea of the amount of information coming back nor the quality of the connection) so to keep things running smoothly they are delayed until JavaScript has wrapped up the current queue. This delay can have large impacts if you are not prepared for them. That is why we use .then()or awaitwhen working with fetches, it tells JavaScript to wait for the fetch to complete, then proceeded with the relevant instructions.

So, what about setting state in React? As I mentioned this is also an asynchronous activity, and according to the React Docs on this topic, setState() activities may be batched. This can lead to some very strange bugs if you are not aware and careful.

For example, the below function takes in a newPokemon object and adds it to an existing array of pokemon in the state.

addNewPokemon = (pokemon) => {
this.setState({pokemon:[...this.state.pokemon, newPokemon]})
}

It's clean, simple, and would work, most of the time. But, if your application is making several setState calls in short order, it may get batched and from the time that you call addNewPoemon to when the state is finally set, this.state.pokemon might have changed and you could get some really strange results. Lucky for us the developers at Facebook who first created React gave us a tool to deal with this (and also part of why we use setState).

Previous State

When setState is called, you have the ability to access the state at the time the function was first called, as well as the props available then.

this.setState((previousState, previousProps) => {
return{ //your state setting stuff here }
}
);

As you can see above the syntax is very similar to a typical setState but before the {} you add an arrow function where you get access to the previous state and previous props. Per the React docs “it also works with regular functions” as shown below, but who likes extra typing?

this.setState( function( previousState, previousProps ) {
return { //your state setting stuff here }
}
);

These snapshots of the state and props will be held in memory, ready to be used when React finally gets around to updating state. So armed with this new knowledge, let's refactor addNewPokemon to be more resilient.

addNewPokemon = (pokemon) => {
this.setState(oldState => {
return { pokemon: [...oldState.pokemon, newPokemon]}
})
}

Cool, that is easy (course that makes sense, easy is the goal of React). We now have a function that will add a pokemon to our array and is much less likely to run into issues if we call lots of state-changing actions in a short time span. One note, now that we have put a function inside of our .setState we need to be explicit about what we want to return to .setState.

So, when you setState in your React applications, don’t forget to use the previous state in order to be prepared if React needs you to wait.

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