Careeroza
Common Causes and Fixes
Leaking Timer vs Fixed Timer
Example
Show both clocks, then hide them. The leaking clock keeps logging after it is gone; the fixed one stops because its effect returns a cleanup.
import { useState, useEffect } from 'react';
function LeakyClock() {
useEffect(() => {
setInterval(() => console.log('leaky clock still ticking...'), 1500); // no cleanup!
}, []);
return <p>Leaky clock</p>;
}
function FixedClock() {
useEffect(() => {
const id = setInterval(() => console.log('fixed clock tick'), 1500);
return () => clearInterval(id); // cleanup on unmount
}, []);
return <p>Fixed clock</p>;
}
export default function App() {
const [show, setShow] = useState(false);
return (
<div>
<button onClick={() => setShow(!show)}>{show ? 'Hide' : 'Show'} clocks</button>
{show && <><LeakyClock /><FixedClock /></>}
</div>
);
}Result
Causes and Fixes
| Cause | Fix in the effect cleanup |
|---|---|
| setInterval / setTimeout | clearInterval(id) / clearTimeout(id) |
| window.addEventListener | window.removeEventListener with the same function |
| WebSocket / subscriptions | socket.close() / unsubscribe() |
| fetch still running after unmount | AbortController.abort() |
| Global arrays/maps that keep growing | remove entries, limit size |
| Closures holding big data | do not keep large objects in long-lived callbacks |
Event Listener Cleanup
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize); // same function reference
}, []);