Suggest an editImprove this articleRefine the answer for “How to cancel a request inside useEffect()?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)If a component unmounts while an async request is still running, it **still completes**, and you'd try to call `setState` on an already removed component, so the request should be canceled in the `useEffect` cleanup function. **Key point:** `fetch` uses `AbortController`, other APIs have their own methods (`clearTimeout`, `socket.close()`, etc.).Shown above the full answer for quick recall.Answer (EN)Image## The problem If a component unmounts (for example, the user left the page) while an async request (`fetch`, `axios`, `setTimeout`) is still running, it **still completes**, and you'd try to call `setState` on an already removed component. The result: a warning in the console extra load on the network a memory leak --- ## The solution - cancel the request in the `useEffect` cleanup function ### Option 1. **With** `AbortController` **(built into fetch)** ```javascript import { useEffect, useState } from "react"; function UserProfile({ id }) { const [user, setUser] = useState(null); const [error, setError] = useState(null); useEffect(() => { // Create a cancellation controller const controller = new AbortController(); const signal = controller.signal; // Run the async request async function loadUser() { try { const res = await fetch(`/api/users/${id}`, { signal }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const data = await res.json(); setUser(data); } catch (err) { // Ignore the error if the request was canceled if (err.name !== "AbortError") setError(err); } } loadUser(); // Return the cleanup function - cancel the request on unmount return () => controller.abort(); }, [id]); if (error) return <p>Error: {error.message}</p>; if (!user) return <p>Loading...</p>; return <p>Name: {user.name}</p>; } ``` **What the code does:** - `AbortController` creates a signal (`signal`) that can be passed into `fetch`. - When `controller.abort()` is called: - `fetch` is aborted instantly; - the promise goes into `catch` with an `{ name: 'AbortError' }` error; - we simply ignore it. This is the **official, standard way** to cancel a `fetch`. --- ### Option 2. **For other async operations** If you're using something that doesn't support `AbortController` (for example, `axios`, `setTimeout`, `WebSocket`): #### a) axios ```javascript useEffect(() => { const controller = new AbortController(); axios.get('/api/users', { signal: controller.signal }) .then(res => setUser(res.data)) .catch(err => { if (axios.isCancel(err)) return; // request canceled console.error(err); }); return () => controller.abort(); }, []); ``` > axios 1.4+ already supports `signal` from `AbortController`, > older versions use `CancelToken` (deprecated). #### b) setTimeout / setInterval ```javascript useEffect(() => { const timeout = setTimeout(() => setCount(c => c + 1), 3000); return () => clearTimeout(timeout); }, []); ``` #### c) WebSocket or EventSource ```javascript useEffect(() => { const socket = new WebSocket("wss://example.com"); socket.onmessage = e => console.log(e.data); return () => socket.close(); }, []); ``` --- ### Option 3. **If the library manages cancellation itself** Libraries like **TanStack Query (React Query)** or **SWR** already: - cancel requests on unmount; - do not call `setState` after unmount; - cache results and reuse them. Example with React Query: ```javascript const { data, isLoading } = useQuery({ queryKey: ['user', id], queryFn: () => fetch(`/api/users/${id}`).then(r => r.json()) }); ``` Here everything is safe out of the box. --- ## SUMMARY | What to do | How | |---|---| | Create a controller | `const controller = new AbortController()` | | Pass the signal into the request | `{ signal: controller.signal }` | | Catch the error in `catch` | `if (err.name === 'AbortError') return;` | | Clean up in the cleanup function | `return () => controller.abort();` | --- ### In short: > An async request inside `useEffect()` should be canceled in the cleanup function, > to prevent updating the state of an unmounted component > and to avoid memory leaks. > `fetch` uses `AbortController`, other APIs have their own methods (`clearTimeout`, `socket.close()`, etc.).For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.