React Memory Leaks: what, why, and how to clean them up!
Tags: javascript, programming, react, reason
Published: 2023-01-24 12:06:07

Intro
All React developers should optimize their applications. Computers don’t have unlimited resources and some browsers are eating a lot of memory even for a few open tabs. If you have, let’s say, 15 tabs open, often you will need more than 8GB of ram to feed that monster browser. And if most of them have memory leaks, then it can really slow down your computer. For React developers, optimization can be a headache if not done properly or not done at all. And often developer team prioritizes optimization as the last task. Refactoring code for optimization after a year of development can be a real pain in the head, believe me. Memory leaks can destroy your project and cost a big amount of money and time. Read this article to understand why and how you can clean up memory leaks.

What is a memory leak?
A memory leak is a commonly faced issue when developing React applications. A memory leak, in React, is a type of resource leak that occurs when an application incorrectly manages memory allocations. That memory, which is not needed anymore, is not released for other processes to use. A memory leak may also happen when an object is stored in a memory, but cannot be accessed by the running code.

Once you have a memory leak, you will get a similar error in console log:

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.

Why should you clean up memory leaks?
Memory leaks are commonly faced issues when developing React applications. It causes many problems, including:

Affecting the project’s performance by reducing the amount of available memory;
Slowing down the application;
Refreshing the page randomly;
Crashing the system;
Overloading database with huge amounts of queries;
An example of why you should fix memory leaks:

Let’s say you are building an application with React and Firebase for database. You create your website which interacts with Firebase a lot. And just after a few hours, you hit 50k read limit of the day. You check that the only user was you. How is that possible?! Now your website is down and will not work for the day just because you didn’t clean up memory leaks.

What causes a memory leak?
A memory leak appears when React applications created subscriptions that were made when a component was mounted and did not unsubscribe them when those components were unmounted.

These subscriptions could be:

DOM Event listeners;
WebSocket subscriptions;
Requests to an API;
Let’s look at a bad example of a component without memory leak clean-up:

In the code snippet above, we have a simple component Cars which when mounted, makes a request to get a data list of car information and sets the value of a car list state to the value gotten from the API.

Scenario:

A user performs an action that triggers an event handler to fetch data from an API.
After that, user clicks on a link, which navigates to another page.
Even though the user is no longer on the last page, data is retrieved from the API and calls function, which updates the now inexistent carList state.
In the long run, this stacks up and eats both your API fetch limit and user’s browser resources.
How to clean up memory leaks?
Modern programming languages and frameworks have techniques for clearing out data that is no longer needed. And react is not an exception.

Basically, we need to remove subscriptions when the component unmounts. For that there are three ways:

1. Using Boolean Flag

The useEffect hook has functionality to do something after a component was unmounted. We simply create variable isMounted and create an if clause to check if data should be fetched or not. As component is unmounted, data is not fetched anymore.

2. Using AbortController

Javascript has AbortController. The AbortController interface represents a controller object that allows you to abort one or more Web requests as and when desired.

Now when a user navigates to a new page, AbortController cancels the request, and memory leak is cleaned up.

3. Using the use-state-if-mounted Hook

There is a great library use-state-if-mounted. It works like useState hook, but it also checks if that component is mounted before updating the state.