The problem ...
When you trigger a fetch request in a component, that request is handled by the browser. If the user navigates away or the component unmounts before the server responds, the request still finishes in the background. This leads to two main issues:
Memory Leaks & State Errors: You might try to update the state of a component that no longer exists (the infamous "cannot update state on an unmounted component" warning).
Race Conditions: If a user clicks a "Next Page" button rapidly, multiple requests fly out. Without cancellation, an older request might finish after a newer one, overwriting your UI with stale data.
Key elements
AbortController: an object created usingAbortController()constructor.AbortSignal: an object that is attached to the asynchronous operation. which can be used to communicate with, or to abort, an asynchronous operation.AbortController.abort(): Aborts an asynchronous operation before it has completed. This is able to abort fetch requests, consumption of any response bodies, and streams.
The following snippet shows how we might use a signal to abort downloading a video using the Fetch API.
When
abort()is called, thefetch()promise rejects with a DOMException named AbortError.If the request is aborted after the
fetch()call has been fulfilled but before the response body has been read, then attempting to read the response body will reject with an AbortError exception.An AbortSignal can only be used once. After it is aborted, any fetch call using the same signal will be immediately rejected.
Check another use cases at: AbortSignal Page (MDN)
React Example (client-side fetching)
You can extract this logic into a custom hook and make it reusable, and you can even create your own custom Promise resolver with built in abort functionality.
TanStack Query library handles the AbortController for you. If a component unmounts or a query key changes while a fetch is in flight, the library automatically triggers the abort signal.