useEffect
Side effects in function components — when and how to use useEffect
Free tier: read the explanation here. Upgrade to Pro for Drills, Speed challenges & Mastery badges.
UpgradeKnowledge Debt detected
You can study this freely — but your score may plateau if these foundations have gaps. The Mastery badge requires them to be solid.
Explanation
useEffect lets you run s after a component renders — data fetching, subscriptions, timers, direct DOM access. The means run once on mount; omitting it means run after every render]controls when the effect runs.
import { useEffect } from 'react';
useEffect(() => {
// runs after render
}, [dependencies]);Dependency array controls when it runs:
- No array: runs after every render
- Empty array `[]`: runs once after the first render (mount)
- `[a, b]`: runs when
aorbchange
useEffect(() => { document.title = count; }); // every render
useEffect(() => { fetchInitialData(); }, []); // once on mount
useEffect(() => { fetchUser(userId); }, [userId]); // when userId changesCleanup function — return a function to clean up subscriptions, timers, etc.: ``jsx useEffect(() => { const id = setInterval(() => setTime(Date.now()), 1000); return () => clearInterval(id); // runs on unmount (or before re-running effect) }, []);
React StrictMode calls effects twice in development (mounts → unmounts → mounts) to help detect missing cleanup.
Common mistake: adding a function to the dependency array that is re-created every render → infinite loop. Use useCallback or move the function inside the effect.
Examples
Data fetching on mount
Re-fetch whenever postId changes by including it in the dependency array
function PostDetail({ postId }) {
const [post, setPost] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/api/posts/${postId}`)
.then(r => r.json())
.then(data => { setPost(data); setLoading(false); });
}, [postId]); // re-fetch when postId changes
if (loading) return <Spinner />;
return <Article post={post} />;
}Cleanup: event listener
Always remove event listeners in the cleanup function to prevent memory leaks
function WindowSize() {
const [size, setSize] = useState({ w: window.innerWidth, h: window.innerHeight });
useEffect(() => {
const handler = () => setSize({ w: window.innerWidth, h: window.innerHeight });
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler); // cleanup!
}, []); // attach once, cleanup on unmount
return <p>{size.w} × {size.h}</p>;
}How well did you understand this?
Next in React
Forms in React