React state and async operations: how to avoid mistakes!
When working with React (especially if you’re new to this wonderful world) you have to keep in mind some crucial aspect while working with component’s state and asynchronous operations.
My first approach to the React library was quite friction-less. It was relatively easy to get started and make things “work”. I started with React in November 2019; in this period I was working on a personal project which I wanted to get ready for publishing as fast as possible. I decided to switch from vanilla JS to React and started translating my project, component after component.
As my brand new React project was growing (in size and complexity) I started arguing with some strange (and maybe unsane?) bugs that I couldn’t explain to myself.
Nowdays every modern web project use async technologies and techniques to create complex but user-friendly web applications, and my project was one of these. But when you start using the same Vanilla JS approach into a React project context, you will not be able to make things work and you will never understand why: your code will just look correct at your eyes!
Let’s talk about code!
Let’s look at a simple scenario you may encountered in your career as web developer: a simple search bar for products (or for whatever you want).
Our simple component looks very nice, isn’t it? Well… it’s not!
Our code look easy to understand:
- We listen for input changes by attaching the handleQueryChange
method to the onChange
event
- In handleQueryChange
we perform a state update and after that we call the doSearch
method
Also if the idea behind this algorithm seems legit, this code will simply not work, and to give you the proof we will simply add a console.log
to the doSearch
method and see what is passed to our API.
Running this simple example we will get a similar output
As you can see it seems like the content typed into the input box is not “in-sync” with the output of our console.log
. The reason behind this strange behavior is that React state is asynchronous. This mean that when the doSearch
method got executed the state wasn’t already updated, it was using an “older state”.
So what’s the solution?
Fortunately, React JS is capable to handle these situations (obviously) and we can make our search bar working with a small code change.
The setState
method we use for each state update usually get 1 parameters, but many newcomers don’t know this method can handle also a second parameter, a callback! This mean we can pass to setState
a function which will get executed when all our state changes are done.
Let’s see the fully working code
Now we call doSearch
method in the callback function we passed as second parameter of setState
. In this way, when we perform a search operation we are sure that this.state.query
point to the correct value!
When use this solution?
Obviously you could use this solution whenever you want. But take in mind that sometimes can just be useless. There are cases where you are performing a state update and immediately executing some other functions which may update the state too, or you will just call some callback function passed with props from parent components.
For example if you are working on a modal component, you may update the state when a “dismiss button” is clicked and you also call the onDismiss
function passed from the parent component. In this case you will probably not care about the state update and you can call the dismiss function whenever you want, also before the “real” state update.
(In my opinion) The only question you should answer to understand if you should use the callback parameter is:
Is this state update important for next instructions? They need an updated state version to work correctly?
If the answer is “yes”, the callback parameter is the correct choice.