๐—ฆ๐˜๐—ผ๐—ฝ ๐—ฅ๐—ฒ๐—ฎ๐—ฐ๐˜ ๐— ๐—ฒ๐—บ๐—ผ๐—ฟ๐˜† ๐—Ÿ๐—ฒ๐—ฎ๐—ธ๐˜€

You see this warning in your console: "Warning: Can't perform a React state update on an unmounted component."

This happens because of async operations inside useEffect.

When you start a fetch call, a race condition occurs. If your component unmounts before the call finishes, React tries to update state on a component that no longer exists. This causes memory leaks.

The Problem:

useEffect(() => { fetch('/api/data') .then(res => res.json()) .then(data => { setData(data); }); }, []);

The component might be gone by the time the data arrives.

The Fix:

You must return a cleanup function. This function runs when the component unmounts.

Method 1: Use a boolean flag.

useEffect(() => { let isMounted = true;

fetch('/api/data') .then(res => res.json()) .then(data => { if (isMounted) { setData(data); } });

return () => { isMounted = false; }; }, []);

Method 2: Use AbortController.

This is the better way. It stops the network request itself.

useEffect(() => { const controller = new AbortController();

fetch('/api/data', { signal: controller.signal }) .then(res => res.json()) .then(data => setData(data)) .catch(err => { if (err.name === 'AbortError') return; console.error(err); });

return () => { controller.abort(); }; }, []);

Why cleanup matters:

โ€ข It prevents unnecessary re-renders. โ€ข It stops state updates on unmounted components. โ€ข It cancels network requests to save bandwidth.

Always use a cleanup function for async operations, subscriptions, or timers. Use AbortController for fetch calls.

Source: https://dev.to/bhargavigajula/stop-memory-leaks-in-their-tracks-react-useeffect-cleanup-explained-47a5