useEffect
useEffect lets a component synchronize with something outside React's render output.
Examples include:
- setting up subscriptions
- updating the document title
- starting and stopping timers
- connecting to browser APIs
- fetching data when a component appears or when a key value changes
Basic Example
import { useEffect, useState } from "react";
function PageTitleCounter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return <button onClick={() => setCount(count + 1)}>Add</button>;
}The effect runs after React commits the render to the screen. The dependency array tells React when the synchronization should run again.
Effects Are Not Event Handlers
Use event handlers for direct user actions.
function handleSubmit() {
sendAnalytics("signup_submitted");
}Do not move this into an effect just because it uses state. Effects are for synchronization caused by rendering, not for every action in the app.
Dependency Arrays
useEffect(() => {
document.title = title;
}, [title]);Every reactive value used inside the effect should be included in the dependency array: props, state, and values declared inside the component.
An empty array means the effect does not depend on any reactive values from the component.
useEffect(() => {
console.log("Component mounted");
}, []);Cleanup
Return a cleanup function when the effect creates something that must be stopped.
useEffect(() => {
const id = setInterval(() => {
console.log("tick");
}, 1000);
return () => clearInterval(id);
}, []);React runs cleanup before the effect runs again and when the component unmounts.
Stale Closures
Effects capture values from the render that created them.
useEffect(() => {
const id = setInterval(() => {
console.log(count);
}, 1000);
return () => clearInterval(id);
}, []);This logs the initial count, not the latest count. Add count to dependencies, use a functional update, or store the latest value in a ref depending on the goal.
What should go in an effect dependency array?
Common Mistakes
Do not use effects to compute values that can be calculated during render.
const filteredItems = items.filter((item) => item.visible);Do not suppress dependency warnings without understanding why. The warning often points to a stale closure or misplaced logic.
Do not forget cleanup for subscriptions, timers, and in-flight async work that can outlive the component.
Practice Challenge
Create a component that starts a timer when isRunning is true and stops it when false.
Requirements:
- use an effect
- include the correct dependencies
- clean up the interval
- avoid reading stale state inside the interval
Recap
useEffect synchronizes React components with external systems. Use it after rendering, list dependencies honestly, clean up resources, and avoid using effects for calculations or direct event responses.