Leaky Components

Avoiding warnings on unmounted react component

Jude Agboola
Charisol Community
4 min readSep 9, 2019

--

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 with clearInterval 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?

  1. A counter component calling setInterval without calling clearInterval
Leaky component — #Example 1

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 :)

Leaky component — #Example2

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

Showing how timers may increase overtime — tap the button multiple times

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

clearing timer with clearInterval

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”

fetching users from Github API

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.

Fixed Example using AbortController

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

canceling promise with bluebirdjs

Axios

canceling promise with 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 and clearInterval when you use them.
  • If the component can get unmounted(e.g by switching routes), cancel promises in componentWillUnmount or useEffect if you use hooks.
  • Avoid using mounted/unmounted checking option especially when using setInterval
  • Use Bluebird, Axios, ….., or RX to handle promise cancellation

Thanks for reading!

--

--

Jude Agboola
Charisol Community

Software Engineer • Occasionally write about software and anything that excites me.