Introduction
When building React applications, we often need to fetch data from APIs. But what happens if a component is removed from the page before the request finishes?
Updating the state of an unmounted component can cause warnings or even memory leaks.
To handle this safely, we use AbortController.
What does “unmounting a component” mean?
Unmounting a component means React removes it from the DOM. This can happen when:
The user navigates to another page or component.
Conditional rendering becomes false
The parent component is removed or updated.
After unmounting, you should not update the component’s state. If a fetch request is still running and tries to update state, React shows this warning:
Warning: Can't perform a React state update on an unmounted component.
This warning indicates a potential memory leak. The fetch continues running even though the component is gone, wasting memory and CPU.
What is AbortController and why use it?
AbortController is a browser API that allows you to cancel an ongoing fetch request.
Benefits:
Prevents warnings when a component unmounts before the request finishes.
Avoids memory leaks.
Saves network and system resources.
When to use it:
When fetching data in a component that can disappear quickly.
When a user can cancel an action or navigate away.
For long-running or multiple concurrent fetch requests.
Practical Example: Custom useFetch Hook with AbortController
Below is a complete example of a custom hook that uses AbortController to safely fetch data:
import { useEffect, useState } from "react";
export interface AlbumDataProps {
userId: number;
id: number;
title: string;
}
export const useFetch = () => {
const [data, setData] = useState<AlbumDataProps[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController(); // 1. Create controller
const fetchData = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/albums",
{ signal: controller.signal } // 2. Connect fetch to controller
);
const albums = await response.json();
setData(albums);
} catch (err) {
if (err instanceof Error) {
// 3. Ignore AbortError if request is canceled
if (err.name === "AbortError") return;
setError(err.message);
} else {
setError("Unknown error");
}
} finally {
setLoading(false); // 4. End loading regardless of outcome
}
};
fetchData();
// 5. Cleanup: cancel fetch if the component unmounts
return () => controller.abort();
}, []);
return { data, loading, error };
};
How it works
State management: data, loading, and error track the request status.
useEffect: Runs the fetch when the component mounts.
AbortController: Creates a controller to cancel fetch if needed.
Signal connection: The signal property links the controller to the fetch. Calling controller.abort() stops the fetch.
Error handling: Regular errors are caught and saved in error. Abort errors are ignored intentionally.
Cleanup function: The return function cancels the fetch when the component unmounts.
Using the Hook in a React Application
Here’s how the hook works in a real app:
import { useState } from "react";
import Button from "./components/Button/Button";
import CardsList from "./components/CardsList/CardsList";
import { useFetch } from "./hooks/useFetch";
function App() {
const { data, loading, error } = useFetch();
const [show, setShow] = useState(false);
return (
<>
<Button setShow={setShow} show={show} />
{show && (
<div>
{loading && <p>Loading...</p>}
{error && <p style={{ color: "red" }}>{error}</p>}
{!loading && !error && <CardsList albums={data} />}
</div>
)}
</>
);
}
export default App;
Explanation:
Clicking the button toggles the list visibility.
When the list is shown, useFetch runs and fetches data.
If the user hides the list before fetch completes, AbortController safely cancels the request.
No warnings are shown, and the app remains stable.
Conclusion
Using AbortController in React ensures that components can safely unmount without causing memory leaks or warnings.
It’s essential for components that fetch data but can appear and disappear quickly, such as toggled lists, modals, or tabs.
You can view the complete project on GitHub (including App.tsx, Button, CardsList, and Card components) to explore the full implementation.
Top comments (0)