Memory leaks cause performance degradation over time.
Common Memory Leak Sources
1. Uncleared Timers
jsx// ❌ Timer not cleared function Timer() { const [count, setCount] = useState(0); useEffect(() => { setInterval(() => { setCount(c => c + 1); }, 1000); // Missing cleanup! }, []); } // ✅ Timer cleared on unmount function Timer() { const [count, setCount] = useState(0); useEffect(() => { const intervalId = setInterval(() => { setCount(c => c + 1); }, 1000); return () => clearInterval(intervalId); }, []); }
2. Event Listeners
jsx// ❌ Listener not removed function WindowResize() { const [width, setWidth] = useState(window.innerWidth); useEffect(() => { window.addEventListener('resize', () => { setWidth(window.innerWidth); }); // Missing cleanup! }, []); } // ✅ Listener removed on unmount function WindowResize() { const [width, setWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = () => setWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); }
3. Subscriptions
jsx// ❌ Subscription not unsubscribed function DataStream() { const [data, setData] = useState(null); useEffect(() => { const subscription = dataSource.subscribe(setData); // Missing cleanup! }, []); } // ✅ Subscription cleaned up function DataStream() { const [data, setData] = useState(null); useEffect(() => { const subscription = dataSource.subscribe(setData); return () => subscription.unsubscribe(); }, []); }
4. Async Operations After Unmount
jsx// ❌ Setting state after unmount function UserProfile({ userId }) { const [user, setUser] = useState(null); useEffect(() => { fetchUser(userId).then(setUser); // Component might unmount before fetch completes! }, [userId]); } // ✅ Using AbortController function UserProfile({ userId }) { const [user, setUser] = useState(null); useEffect(() => { const controller = new AbortController(); fetchUser(userId, { signal: controller.signal }) .then(setUser) .catch(err => { if (err.name !== 'AbortError') { console.error(err); } }); return () => controller.abort(); }, [userId]); }
Detecting Memory Leaks
Chrome DevTools
- Open DevTools → Memory tab
- Take heap snapshot
- Use app for a while
- Take another snapshot
- Compare snapshots
Performance Monitor
- DevTools → More tools → Performance monitor
- Watch "JS heap size"
- If it keeps growing, you have a leak
React DevTools
Look for:
- Mounted components that should be unmounted
- Increasing number of component instances