React - useEffect
useEffect
useEffect
lets you synchronize your component with external systems (DOM, network, timers).
When to use it
- Synchronize with external systems: DOM APIs, event listeners, timers, or network requests.
- Run code after React renders and when specific dependencies change.
- Clean up subscriptions, timers, or listeners on unmount or before the next run.
Try it: Document Title
View source
const { useState, useEffect } = React;
function TitleCounter(){
const [n, setN] = useState(0);
useEffect(() => {
const prev = document.title;
document.title = `Count: ${n}`;
return () => { document.title = prev; };
}, [n]);
return (
<div>
<p>Count is {n}</p>
<button onClick={() => setN(n + 1)}>Increment</button>
</div>
);
}
ReactDOM.createRoot(document.getElementById('try-effect')).render(<TitleCounter />);
Try it: Interval Timer with Cleanup
View source
const { useState, useEffect } = React;
function Ticker(){
const [tick, setTick] = useState(0);
useEffect(() => {
const id = setInterval(() => setTick(t => t + 1), 1000);
return () => clearInterval(id);
}, []);
return <p>Ticks: {tick}</p>;
}
ReactDOM.createRoot(document.getElementById('try-effect-interval')).render(<Ticker />);
Syntax primer (for newcomers)
useEffect(effect, deps)
: Runs after render; re-runs whendeps
change.- Return a function from the effect to clean up (unsubscribe, cancel timers, restore DOM).
- Dependency array must include all reactive values used inside the effect.
Common pitfalls
- Missing dependencies: include all reactive values you read inside the effect in the dependency array.
- Infinite loops: avoid updating state unconditionally inside an effect; use conditions or correct dependencies.
- Manual DOM mutations: prefer refs and React patterns; avoid fighting React’s rendering.
- Leaking resources: always clean up timers, subscriptions, and listeners.
Cleanup
Return a function to clean up resources (e.g., remove listeners, cancel timers).
Vocabulary
- Effect: Code that runs after render to sync with external systems.
- Dependency array: List of values that determine when an effect re-runs.
- Cleanup: Function returned by an effect to undo side effects.
Common pitfalls
- Missing dependencies: if your effect uses a value, include it in the dependency array or explain why it’s intentionally omitted.
- Subscribing without cleanup: always return a cleanup function to avoid leaks (event listeners, intervals).
- Running heavy work in effects: offload CPU-bound tasks; avoid blocking the main thread.
Exercises
- Subscribe to
window.resize
in an effect and show the window width; clean up on unmount. - Implement a ticking clock with
setInterval
and clear it in the cleanup. - Fetch JSON from a public API (e.g.,
https://jsonplaceholder.typicode.com/todos/1
) in an effect; handle loading and error states.
Checks for Understanding
- When does an effect run if the dependency array is empty? What about omitted?
- Why return a function from an effect?
Show answers
- Empty array: runs once after mount; omitted: runs after every render.
- To clean up subscriptions, timers, or DOM changes and prevent leaks.