What’s in here?
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.
- Asynchronous signal and Abort Controller
- Component lifecycle in React and why you should clean up your mess
- Abort of asynchronous signal for events triggered by a component mount
- Abort of asynchronous signal for events triggered by user interactions
- Some thoughts and repo with code
Asynchronous (async) functions and Abort Controller
Asynchronous programming uses such syntax as
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.
Before I’ll get to asynchronous functions a few words about synchronous ones :)
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.
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.
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.
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?
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
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, un-mounting 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 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.
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.
The next step is an
async function that handles
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!
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?
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.
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 :)
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.