Avoiding Race Conditions when Fetching Data with React Hooks

Nick Scialli
HackerNoon.com
4 min readApr 15, 2019

--

Man jumping over clock

About a month ago, I posted an example of fetching data using React Hooks to Twitter. While it was well-intended, Dan Abromov (of the React core team) let me know that my implementation contained a race condition. Consequently, I promised to write a blog post correcting my implementation. This is that post!

Note: If this article helps you, please help spread the word by lending a clap (or 50)! 👏👏

***

Get quick JavaScript tips in your inbox by signing up for my free newsletter!

***

Setup

In our example app, we are going to fake-load people’s profile data when their names are clicked. To help visualize the race condition, we’ll create a fakeFetchfunction that implements a random delay between 0 and 5 seconds.

Initial Implementation

Our initial implementation will use buttons to set the current profile. We reach for the useState hook to implement this, maintaining the following states:

  • person, the person selected by the user
  • data, the data loaded from our fake fetch based on the selected person
  • loading, whether data is currently being loaded

We additional use the useEffect hook, which performs our fake fetch whenever personchanges.

If we run our app and click one of the buttons, our fake fetch loads data as expected.

Hitting the race condition

The trouble comes when we start switching between people in quick succession. Given the fact that our fake fetch has a random delay, we soon find that our fetch results may be returned out of order. Additionally, our selected profile and loaded data can be out of sync. That’s a bad look!

Clicking buttons quickly and hitting the race condition

What’s happening here is relatively intuitive: setData(data) within the useEffect hook is only called after the fakeFetch promise is resolved. Whichever promise resolves last will call setData last, regardless of which button was actually called last.

Canceling previous fetches

We can fix this race condition by “canceling” the setData call for any clicks that aren’t most recent. We do this by creating a boolean variable scoped within the useEffect hook and returning a clean-up function from the useEffect hook that sets this boolean “canceled” variable to true. When the promise resolves, setData will only be called if the “canceled” variable is false.

If that description was a bit confusing, the following code sample of the useEffecthook should help.

Even if a previous button click’s fakeFetch promise resolves later, its canceled variable will be set to true and setData(data) will not be executed!

Let’s take a look at how our new app functions:

Clicking buttons quickly and avoiding the race condition

Perfect — No matter how many times we click different buttons, we will always only see data associated with the last button click.

Full code

The full code from this blog post can be found below:

--

--

Nick Scialli
HackerNoon.com

Husband, dog dad, coffee monster. Software engineer at the @usds! Opinions are my own and not the views of my employer. https://github.com/nas5w