Avoiding Memory Leaks

Learn how to mitigating memory leaks in React by using refs to handle asynchronous operations and prevent unwanted updates after component unmount.

Addressing memory leaks in async operations

Historically speaking, a ref was created to hold a DOM element, but people later found it very effective in addressing tricky problems. One problem is memory leaks, which happen when performing an async action. The thing about an async operation is that the callback function gets invoked later. By the time the callback is handled, there's a chance the component (or any variable associated with the component) is not valid anymore.

Let's say we fetch an API and display the result as text:

const Title = () => {
// State variable 'text' to store the fetched text
const [text, setText] = useState("");
// useEffect to fetch data from "https://google.com" on component mount
useEffect(() => {
// Fetch data from the specified URL
fetch("https://google.com")
.then(res => {
// Extract the title from the response and set it in the state
return res.text();
})
.then(title => {
setText(title);
})
.catch(error => {
// Handle errors, such as network issues or CORS restrictions
console.error("Error fetching data:", error);
});
}, []); // Empty dependency array ensures the effect runs only once during component mount
// Render an h1 element displaying the fetched text
return <h1>{text}</h1>;
}
Fetches the title of Google.com

The preceding code is a common fetch process, but there's a memory leak lurking out there. When it happens, the browser outputs the following message:

Press + to interact
Memory leak warning message
Memory leak warning message

Although React is efficient enough to display it as a warning message under development build, it's actually an error, as it indicates a memory leak in the message. The strange thing about this leak is that most of the time, the UI continues to function even after the message. So, should we ignore this message? Absolutely not.

Let's build the crime scene and try to understand what exactly happens under this message:

const App = ({ flag }) => {
// If 'flag' is true, render the Title component; otherwise, render nothing
if (flag) return <Title />;
// If 'flag' is false, return null to render nothing
return null;
}
A React component conditionally rendering the Title component based on the flag prop

Say we have an ...