Working with React for over eight years, I have seen the library evolve from complex class components to the sleek efficiency of Hooks.
One of the most common hurdles I see developers face is the transition from componentWillUnmount to the functional world.
Understanding how a component “cleans up” after itself is vital for building high-performance applications that don’t leak memory.
In this tutorial, I will show you exactly how to handle the unmount phase in functional components using firsthand examples I’ve used in production.
Understand the Cleanup Function in React
In the old days of React, we used a specific lifecycle method to handle the moment a component left the screen.
With functional components, we use the useEffect hook to manage this logic.
When you return a function inside useEffect, React treats that returned function as the “cleanup” mechanism.
It runs right before the component is removed from the DOM, allowing you to stop processes that are no longer needed.
Method 1: Clear Subscriptions for Real-Time Financial Data
In many US-based fintech applications, we often subscribe to real-time price feeds for stocks or cryptocurrencies.
If you don’t unsubscribe when the user navigates away from the dashboard, the app will keep trying to update a component that no longer exists.
Here is the full code to handle a simulated stock price subscription:
import React, { useState, useEffect } from 'react';
const StockTicker = () => {
const [price, setPrice] = useState(150.25);
useEffect(() => {
// Simulating a connection to a New York Stock Exchange data feed
const stockSocket = setInterval(() => {
const volatility = (Math.random() - 0.5) * 2;
setPrice((prevPrice) => parseFloat((prevPrice + volatility).toFixed(2)));
console.log("Fetching latest NYSE data...");
}, 1000);
// This is the cleanup function that runs on unmount
return () => {
console.log("Unmounting: Closing NYSE data socket.");
clearInterval(stockSocket);
};
}, []); // Empty dependency array ensures this runs once on mount
return (
<div style={{ padding: '20px', border: '1px solid #ddd' }}>
<h2>Live Market Data (USA)</h2>
<p>Symbol: <strong>AAPL</strong></p>
<p>Current Price: ${price}</p>
</div>
);
};
export default function MarketDashboard() {
const [showTicker, setShowTicker] = useState(true);
return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
<h1>Wall Street Analytics Tool</h1>
<button onClick={() => setShowTicker(!showTicker)}>
{showTicker ? "Close Ticker" : "Open Ticker"}
</button>
{showTicker && <StockTicker />}
</div>
);
}You can see the output in the screenshot below.

I have found that explicitly clearing these subscriptions is the best way to prevent the “memory leak” warning in the console.
Method 2: Remove Event Listeners for Responsive Layouts
I often work on dashboards that need to track window resizing to adjust data visualizations or navigation menus.
If you add a window.addEventListener in your component, it stays in the browser’s memory even after the component is gone.
Below is an example of tracking the window width for a mobile-responsive US Retailer dashboard:
import React, { useState, useEffect } from 'react';
const WindowWidthTracker = () => {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => {
console.log("Resizing window for Retailer Layout...");
setWidth(window.innerWidth);
};
// Attaching the listener to the global window object
window.addEventListener('resize', handleResize);
// Cleanup: Removing the listener when the component unmounts
return () => {
console.log("Cleaning up: Resize listener removed.");
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div style={{ backgroundColor: '#f4f4f4', padding: '15px' }}>
<h3>Responsive View Status</h3>
<p>Current Width: {width}px</p>
<p>Mode: {width < 768 ? 'Mobile View (USA)' : 'Desktop View (USA)'}</p>
</div>
);
};
export default function AppPortal() {
const [isActive, setIsActive] = useState(true);
return (
<div style={{ padding: '40px' }}>
<h2>Retail Management System</h2>
<button onClick={() => setIsActive(!isActive)}>
Toggle Tracker Component
</button>
{isActive && <WindowWidthTracker />}
</div>
);
}You can see the output in the screenshot below.

In my experience, failing to remove these listeners leads to sluggish performance after a few minutes of usage.
Method 3: Abort API Calls in a Healthcare Search App
When building patient search tools for US healthcare providers, I often encounter “race conditions.”
This happens when a user types a name, the search starts, but the user navigates away before the results return.
To handle this, I use the AbortController inside the useEffect cleanup function.
import React, { useState, useEffect } from 'react';
const PatientSearch = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
const fetchPatientRecords = async () => {
try {
setLoading(true);
// Simulating a fetch to a secure medical database
const response = await fetch('https://jsonplaceholder.typicode.com/users/1', { signal });
const result = await response.json();
setData(result);
setLoading(false);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Fetch aborted: Component unmounted.');
} else {
console.error('Fetch error:', err);
}
}
};
fetchPatientRecords();
// Cleanup: Abort the fetch if the user closes the search modal
return () => {
controller.abort();
};
}, []);
if (loading) return <p>Accessing Secure HIPAA Database...</p>;
return (
<div style={{ color: 'navy' }}>
<h4>Patient File Retrieved</h4>
<p>Name: {data.name}</p>
<p>Provider ID: {data.id} (Verified)</p>
</div>
);
};
export default function MedicalPortal() {
const [isSearching, setIsSearching] = useState(false);
return (
<div style={{ margin: '20px' }}>
<h1>US Healthcare Provider Portal</h1>
<button onClick={() => setIsSearching(!isSearching)}>
{isSearching ? "Cancel Search" : "Search New Patient"}
</button>
{isSearching && <PatientSearch />}
</div>
);
}You can see the output in the screenshot below.

This cancels the network request immediately when the component unmounts, saving bandwidth and processing power.
Why Handling Unmount Matters
In my 8 years of development, the most common bugs I’ve fixed are related to “zombie” processes.
If your code tries to call setState on an unmounted component, React will warn you in older versions and simply fail silently in newer ones.
Both scenarios are bad because they indicate your application is still consuming memory for something the user isn’t seeing.
By consistently returning a cleanup function in your useEffect, you ensure your app stays fast and responsive.
Key Takeaways for React Developers
Always remember that the cleanup function runs not just on unmount, but also before the effect runs again (if dependencies change).
For simple “unmount-only” logic, keep the dependency array empty ([]).
If you are using intervals, timeouts, or event listeners, the cleanup function is your best friend.
You may also read:
- How to Pass Children to a Component in React
- How to Build a React Login Component
- How to Fix React Component Not Defined Error
- How to Create a Reusable Button Component in React

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.