DEV Community

Cover image for 🍦 Cancel Properly HTTP Requests in React Hooks and avoid Memory Leaks 🚨
Victor de la Fouchardière
Victor de la Fouchardière

Posted on • Edited on

🍦 Cancel Properly HTTP Requests in React Hooks and avoid Memory Leaks 🚨

Hello guys ! 👋

Today, let's take a look at cancelling a web request with fetch and Abort Controller in React Hooks! 🤗

When we work with Fetch to manage data, we sometimes want to cancel the request (e.g. when we leave the current page, when we leave a modal, ...).

In the example below 👇, we fetch the data to display when switching route. But, we leave the route/page before the fetch is completed.

React hooks memory leaks 1

🚸 Demo 1/2

React hooks memory leaks 2

🚸 Source code 2/2

We just saw a memory leak in action! 💪🏼 Let’s see why this error occurred, and what it exactly means.

WHY A MEMORY LEAK?: We have a component that performs an asynchronous fetch(url) task, then updates the state of the component to display the elements, BUT we unmount the component before the request is even completed. The state of the unmounted component is updated (e.g. setUsers, setState), which follows a memory leak.

🚀 Let's use the new AbortController API !

Abort Controller allows you to subscribe to one or more Web Requests with the ability to cancel them. 🔥

Basics of AbortController

First of all, let's create a new AbortController object instance.

Abort Controller API

Now, we can access to controller.signal.

"The signal read-only property of the AbortController interface returns an AbortSignal object instance, which can be used to communicate with/abort a DOM request as desired." MDN Source

Let's see how to use it 💪

Abort fetch example 1

And finally, if we want to cancel the current request, just call abort(). Also, you can get controller.signal.aborted which is a Boolean that indicates whether the request(s) the signal is communicating with is/are aborted (true) or not (false).

Abort fetch example 2

❗️ Note: When abort() is called, the fetch() promise rejects with a DOMException named AbortError.

Yeah, you just learned how to cancel a Web Request natively! 👏

🤩 Let's do this with React Hooks !

BEFORE

Below is an example of a component that retrieves data in order to display them:

Memory leak react hooks 1

If I leave the page too fast and the request is not finished: MEMORY LEAK

Memory leak react hooks 2

AFTER

So let's useEffect to subscribe to our fetch request and to avoid memory leaks. We use the clean method of useEffect for calling abort() when our component unmount.

No memory leaks react hooks

Now, no more memory leaks! 😍

Example abort controller memory leaks

As always, feel free to reach out to me! 😉

You can check this demo on abort-with-react-hooks.vercel.app.
Also, here the source code of this article in this gist.

Cheers 🍻 🍻 🍻

If you enjoyed this article you can follow me on Twitter or here on dev.to where I regularly post bite size tips relating to HTML, CSS and JavaScript.

Top comments (19)

Collapse
 
tylerlwsmith profile image
Tyler Smith

Damn this is a really cool article. I've run into that memory leak error before and now I understand what it is I'm looking at. Thank you for writing this!

Collapse
 
viclafouch profile image
Victor de la Fouchardière

A pleasure Tyler !!

Collapse
 
nans profile image
Nans Dumortier • Edited

Heya !
Thanks for this well detailed article!!
I was facing the exact same issue, so I created this hook :
npmjs.com/package/use-state-if-mou...
It basically works like React's useState, but it only updates the state if the component is mounted !
I thought it's an easy and elegant solution for that issue, what do you think ?

Collapse
 
viclafouch profile image
Victor de la Fouchardière

Yeah that's an idea ! I love that !! :)

Collapse
 
hemant profile image
Hemant Joshi

What if i use redux? and use Axios rather than fetch?

Collapse
 
viclafouch profile image
Victor de la Fouchardière

It's the same approch for redux. And I think Axios provide a cancel function.

Collapse
 
hemant profile image
Hemant Joshi • Edited

But I don't see a need for, never used Cancel kind of fun before also built some stuffs in React-Redux never saw console with such errors.......

const listArticles =()=> async(dispatch)=>{


    try{
        dispatch({type: ARTICLE_LIST_REQUEST});
        const {data}= await axios.get('https://dev.to/api/articles?username=hemant')
        dispatch({type:ARTICLE_LIST_SUCCESS, payload: data})
    }
    catch(error){
        dispatch({type:ARTICLE_LIST_FAIL, payload:error.message})

    }
}

I feel like your talking about dispatch........

Thread Thread
 
hemant profile image
Hemant Joshi

+1. I am really new to React about 3-4 Months and would like to know in redux

Thread Thread
 
gautamkrishnar profile image
Gautam Krishna R

This thing happens every time if you are using react hooks: reactjs.org/docs/hooks-intro.html

Thread Thread
 
hemant profile image
Hemant Joshi

Thanks × 1000 👍.

Collapse
 
gautamkrishnar profile image
Gautam Krishna R

This is one of the problem all people face when they are getting started with React. Thanks a lot for putting this out here so that it will help someone.

Collapse
 
viclafouch profile image
Victor de la Fouchardière

Thank you Gautam !

Collapse
 
sqlrob profile image
Robert Myers

In the final example, why use [fetchPosts] over []?

Collapse
 
viclafouch profile image
Victor de la Fouchardière • Edited

Because I use fetchPosts which is a dependency in my useEffect. But fetchPosts will never updated (it's a useCallback with []as dependency) so useEffect will not be executed a second time.

Collapse
 
sqlrob profile image
Robert Myers • Edited

Right, I understand why it works, but why pick one over the other? Would it be faster to not wrap in useCalllback and use []? This is a fetch, so I guess any savings would be washed away in network.

Thread Thread
 
viclafouch profile image
Victor de la Fouchardière

I want to have a function for getting my data. Maybe in the future I would like to have a button to "refresh" my data. And call "fetchPosts" by clicking on it. Or maybe, for my future pagination by adding page as a new parameter of my function fetchPosts.

Collapse
 
ivivanov18 profile image
Ivan Ivanov

Very nice article! Merci!

Collapse
 
viclafouch profile image
Victor de la Fouchardière

🤗

Collapse
 
kbiedrzycki profile image
Kamil Biedrzycki

Looks like it's flagged as experimental (not sure why) but still worth to mention compatibility table caniuse.com/#feat=abortcontroller around this feature.