Leaky Components
Avoiding warnings on unmounted react component
Takeaway
After reading this, you should understand why React throws this error at you, why you should fix it, and ways to fix it.
‘’Can’ t Perform a React state update on an unmounted component’’
When I started out with React I never took the time to understand why React throws this error. I soon discovered that this was mostly caused by:
- Some fulfilled Promises or async operation trying to update the state of an unmounted component
- calling
setInterval
on mount without removing it withclearInterval
when the component is being unmounted - calling
this.forceUpdate
to forcefully rerender a component an unmounted component.
Let’s reproduce this error with few examples, Shall we?
- A counter component calling
setInterval
without callingclearInterval
2. Updating state after promise fulfills, but sadly the component is unmounted — make sure to click the button fast enough so I don’t look like I'm lying :)
What risk does this pose to your react app and how can we solve it?
Issues from memory leaks
React isn’t just throwing this warning at you because it could not update the state, take a look at the error again
“ 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 the componentWillUnmount method ”
React is trying to let you know that this can cause some performance impact on your application because it introduces memory leaks over time for your application.
Having a single instance of this error may not cause any noticeable performance issues. However, having a long list of components throwing this error may cause noticeable issues. Imagine having a component updating its’ local state every sec. Mounting an unmounting it can cause it to create many wasted timers which will eventually slow down your application thanks to how JavaScript execution works.
Here is a typical example. This example shows that a new timer gets created when the component remounts. Click the button multiple times to see this in action
How can you avoid warning on unmounted react component
Anytime you have asynchronous stuff going on and then a callback that updates the state, you want to see if there is some way you can prevent the callback from being called when the component isn’t mounted. This happens mostly when you are doing asynchronous stuff. If you are using await
then you need to make sure that any state update preceding await
should only be done when the component is mounted.
Always make sure you remove timers, cancel subscriptions and the promises in componentWillUnmount
. Since function component doesn’t have lifecycle methods, you can return a function inside useEffect
which is equivalent to componentWillUnmount
in a class component.
Check if the component is mounted before updating the state
We can use a variable to hold mounted/unmounted state of a component. This may not be the best fix because timers, asynchronous operations still continue to hang around. Here is an example.
Using clearTimeout , clearInterval
To fix the counter example, we can simply clear the timer before the component unmounts (componentWillUnmount)
using clearInterval
or clearTimeout
if we were using setTimeout
Canceling Promises
All thanks to the TC39 guys for making promises uncancellable. This means when you kick-off a promise the is no way to say “Hey promise! don’t bother I no longer need your response”
The callback passed into then
/catch
still gets pushed on the call stack since there is no way to stop it. There has to be a way to prevent this. Follow along as we explore some options.
AbortController
We can use a new browser API — AbortController as a workaround for canceling promises. I’ll fix the second example by using the AbortController API.
Other available ways to cancel promises
Most libraries like Axios, Bluebird, RX provide a way to cancel a promises directly without writing any complicated code. Let’s explore some examples.
Bluebird
Axios
Will cancellable promises be available in JavasScript soon?
Not Really. A few years ago a smart dude Domenic wrote a proposal on cancellable promise which is now withdrawn for some reasons.
with this, it doesn’t seem like JavaScript will provide native support for cancellable promises anytime soon, but we know several workarounds right ;)
Summary
- Most of the time,
clearTimeout
andclearInterval
when you use them. - If the component can get unmounted(e.g by switching routes), cancel promises in
componentWillUnmount
oruseEffect
if you use hooks. - Avoid using mounted/unmounted checking option especially when using
setInterval
- Use Bluebird, Axios, ….., or RX to handle promise cancellation