React: Stop checking if your component is mounted

Matthieu Kern
Doctolib
6 min readSep 22, 2021

--

This article will provide solutions to the “Can’t perform a React state update on an unmounted component” warning and why a call to setState when your component isn’t mounted is not a valid solution.

Update 08/10/2021: Between the writing and publication of this blog post, the setState warning has been removed from the React codebase. This change will be included in the next version of React. This article remains correct and highlights why this change has been made. Quoting Dan Abramov (source):

The post is sort of correct though. We’re removing the warning because people work around it with isMounted which defeats the purpose. So we might as well not warn. But the proper solution is cancellation. Or ignoring if you’re lazy about cancelling.

Original story: As with any other React application, we have to deal with asynchronous state updates at Doctolib, which sometimes lead to a “setState warning”. These are even more visible thanks to our thorough testing strategy, with more than 30,000 end-to-end tests that will fail on any kind of warning. Here is how we overcame the tedious process of getting rid of all our setState warnings, without checking if our components are mounted.

A bit of context

Like most React developers, you have probably encountered at least once in your life the “setState warning”:

Warning: Can’t perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

But did you ever wonder what was actually happening and why you were getting this? If it’s there, it must be for a reason, right?

Here is a simplified timeline of what’s happening and why you get this warning:

Most of the time, this happens after you started a network query, unmounted the component that started this query, then handled the response to this query in a callback that also updates the state of your component. And as you call your state-dependent function, React realizes that your component is already unmounted, so ignores the update to its state and warns you that you probably have a memory leak.

Introducing the anti-pattern

If like me your first reaction was to search the internet for a solution to this annoying warning, you probably found an answer similar to this, in its simplest version, using the cleanup mechanism of useEffect:

It is even possible to extract this in a hook for further use, as you may find in multiple accepted StackOverflow answers:

Bad news: this is absolutely not a good way to deal with this warning and most resources you can find on this subject on the internet are wrong.

This code is doing nothing apart from hiding the warning. React is already doing a check internally and does not update a component that has been unmounted (that’s why you have the warning). This only moves the check to suppress the warning:

Why is this bad?

By using isMounted or similar mechanisms, a reference is kept on the unmounted component for no reason. This is a source of memory leaks.

Also, your application will continue to execute the queries and their related callbacks. This won’t have any effect: the result of all this processing will be ignored eventually.

Finally, it’s just a very complex way of hiding a warning. If your sole goal is to ignore it, you have way more efficient ways to do it.

You can find more information about this in an article from 2015 on the React technical blog. Note: even though there is a suggestion on reimplementing isMounted in this article, it’s only given as a transitional phase, not as a long term solution. Also, the AbortController interface didn’t exist at that time.

And if you’re not convinced yet… In order to present the warning, React already uses a check that the component is mounted. If checking this was the way to go, they wouldn’t warn you about it.

Technically, React cuts off the component from the tree here when unmounting it, then checks here in the FiberWorkLoop if the component is still in the tree, to finally trigger the warning here if not. In this article, I’ll simplify this by saying that React “checks if the component is mounted under the hood”, which is a technical approximation.

What am I supposed to do instead?

Cancel your queries! For simple use cases that are using the Fetch API, you should use the AbortController API to cancel your queries.

I can understand if your first reaction is on the verbosity of this piece of code. But what happens here is the key to suppress this warning the right way: on line 13, when calling abort, the query stops running. Not only is its result ignored, but the then on line 4 will never be called. Instead, the code will go through the catch block, in which we can catch an AbortError and properly dispose of the code. No setState involved.

To know more about the AbortController and the WHATWG discussion on how to abort a fetch query, this great article from the Google Developers blog gives a view on the current state of the implementation and its history.

What about non-fetch async operations?

When I presented this in our Tech Time meetup, some concerns got raised as some of our codebase communicates with custom, desktop based APIs or with the Tanker SDK we use to perform end-to-end encryption on most of our data.

For this purpose, you should implement a mechanism to cancel your async operations. It’s up to you to decide how, however, the AbortController API is very simple and adaptive, and is available mostly everywhere (on most modern browsers and in Node.js), so this would be the recommended way to treat this problem in your custom interfaces. For instance, that’s what AWS did with their Javascript SDK, as well as Azure.

Some other Web APIs are also using similar implementations as the AbortController, like IndexedDB that implements an abort method on a transaction: transaction.abort() and the FileReader API that provides the instanceOfFileReader.abort() method.

Conclusion

When working with the Fetch API, cancelling your queries gets easy with the AbortController. This solves 90% of the “setState warning” cases. And it gets easy to abstract the logic of the AbortController associated with the React lifecycle in a custom hook. We use this hook at Doctolib:

So remember: if you find yourself checking that your component is mounted, it’s already too late.

If you want more technical news, follow our journey through our docto-tech-life newsletter.

And if you want to join us in scaling a high traffic website and transforming the healthcare system, we are hiring talented developers to grow our tech and product team in France and Germany, feel free to have a look at the open positions.

--

--