React, Abort Controller and onClick async calls

Published: 2020/02/02

What is Abort Controller in JavaScript Web Apps and how to use it in React to abort async calls? Theory and various use cases.

What’s in here?

On the very beginning I talk about some basic theory behind asynchronous functions in JavaScript and how they work.

Then a bit about Abort Controller and why to use it in a first place?

When the introduction is over I’ll show two use cases in React- the first is a basic usage when you data are fetched on a component mount and the second one is for async requests triggered by user interaction like eg. onClick.

Article overview:

  1. Asynchronous signal and Abort Controller
  2. Component lifecycle in React and why you should clean up your mess
  3. Abort of asynchronous signal for events triggered by a component mount
  4. Abort of asynchronous signal for events triggered by user interactions
  5. Some thoughts and repo with code

Asynchronous (async) functions and Abort Controller

As you probably know JavaScript supports some particular kind of programming - asynchronous programming. If for some reason you’re still not aware of it come back to this article in a while :)

Asynchronous programming uses such syntax as async-await or then and the main reason you want to use it is to communicate with a server that stores data you want to render in your app.

Synchronous functions

Before I’ll get to asynchronous functions a few words about synchronous ones :)

JavaScript is a single-threaded programming language which pretty much means it executes only one function at the time.

All functions in your app are placed in a special queue that is called callstack and during an execution phase functions are removed from a callstack once completed. That what synchronous means.

abort-callstack-1-min.png

Asynchronous functions

The problem with synchronous approach is that sometimes we want eg. send a request to an API and we might wait (no one knows how long) before we can get any response from a server. Since we wait, a function is not completed so our callstack is blocked. That why asynchronous programming is helpful :)

As an answer for that challenge Web API has special methods/interfaces that are able to take control over asynchronous operations. That means that a User Agent - browser unblocks a callback so other stuff can execute. Then once any data comes back from a server it puts back a response to our callstack as a callback function.

abort-callstack-2-min.png

Fetch API

One of the interfaces used to communicate with a server API is Fetch API. It has at least one great feature - it can use the AbortController. Using fetch method in your app tells a browser it should use Fetch API.

AbortController

The AbortController is a special object/interface that contains a property signal. This property can be added to asynchronous function using fetch as one of the options. This binds a particular signal with a particular function. But why to do that?

Another AbortController’s method is abort() which is able to cancel execution of a function. It means that if a server responds a browser knows to ignore that response and it won’t pass a callback to our callstack. Ok, but what is a use for it?

Component lifecycle in React and why to clean up?

All (?) modern JavaScript frameworks/libraries are based on components. Components are reusable elements that can be used in any part of an app (assuming they are built in that way). You can easily say that modern web apps are built in a modular way.

Each component has its own lifecycle - a moment when it’s rendered, a moment when it’s destroyed (disappears from user screen) and everything that comes in between the two.

Lifecycles in React

In class components access to lifecycle can be given by using methods like componentDidMount() or componentWillUnmount() In my opinion those names are quite self-explanatory :)

Since hooks were introduced we can get access to functional components lifecycle by using useEffect. In regards to how it is used it can behave like detector of events of mounting, unmounting or updating a component. Some basic example:

Why to clean up?

There is some sort risk in using async functions that try to update state of a component. What is it? A callback function might come back but a particular component that initialise async call can be already unmounted!

What happens in that situation? You might get warning about memory leakage:

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op

So it somehow affects app’s performance. I think it’s a bit far from best practices :) This article tackles the issue quite nice.

To avoid that scenario you need to cancel all subscriptions and async calls when a component gets unmounted!

abort-cleanup-min.jpg

Abort of signal initialised on a component mount

Ok, once we covered a basic theory let’s check a case where you want to fetch some data from API straight after a component is mounted:

On a first glance it seems to be a bit complicated but in fact it’s probably one of the most basic examples of using AbortController :) Simply before a component is unmounted I call method AbortController.abort(). That’s it!

Abort of signal initialised by a user interaction

Hypothetically, what if a user wants to get some data from a server by clicking on a button (or by any other interaction)? If a connection is weak and internet slow a user might get annoyed and go to another view. How to cancel that kind of signal? The challenge here is that you need to pass a unique signal to useEffect but this signal is actually initialised outside useEffect - in function that handles onClick action. I know it might sound simple but there is a catch…

As far as I’m aware there is no built in method in React that can handle that scenario (maybe some library?) so what would you say about something like that!

Fetch as a Custom Hook

First step is to create custom Hook that can accept a signal as a parameter or simply provide a unique one. Then it returns both fetch method and an abort method:

So basically now you can bind any signal to your async call or use default one and easily consume it.

React Hooks

Now three simple hooks inside your component:

The first one is just provider of unique parameter to the API Hook.

The second one is our API Hook where we get our API call function and unique method to abort our signal.

The third hook is an array where I store all my signals.

Click Handler

The next step is an async function that handles onClick action:

What is a key in terms our problem here is passing our abort method to the array with unshift() method. Then I simply get my data and update a state.

Update of useEffect

Now I’ll update useEffect hook created in the previous example. There isn’t much left. I just create a function abortClickRequests that maps through my array with signals and call abort() on each one.

When a component is going to be destroyed I simply call predefined function and the game is over!

Thoughts

Exactly… In general this implementation is rather basic and was more about presenting the concept :)

The first disadvantage of this code I see is that I abort all signals inside the table instead of only those that are 100% not finished. It can be optimised I guess.

The second thing is that it’s actually hard (?) to test whether those signals are really aborted. In theory it all should be fine but how can you be 100% sure?

I don’t know maybe all this effort was actually pointless because there is some sort of library of a feature that handles the situation? But then comes in never ending conversation about overusing libraries

You can say that this problem is not a problem and one shouldn’t worry about it :D

I’m not sure… Any other thoughts?

More

If you want to do some more research in the subject you can start from my repo I created for this art. It has working implementation.

Here are a few resources that inspired me - RWieruch, Spec, SLorber.

In general if you think this article is nothing but a bunch of nonsense please do not hesitate to share that in the comments section :)

Takeaway

In this art I explained what is callstack and how asynchronous code is executed in JavaScript.

You know what AbortController is and how to abort asynchronous functions.

The last part was about use cases in React. Now you should know how to abort asynchronous calls triggered by various user interactions using React Hooks.

Bartek Cis

I'm a JavaScript Developer. I like JS world. I adore clean design and love to write articles for my blogs :)