useEffect Hook
Managing side effects in React components
What you'll learn
Run side effects with the useEffect hook
Understand the dependency array and when effects re-run
Clean up subscriptions, timers, and event listeners
Separate concerns with multiple useEffect calls
Avoid infinite loops by correctly specifying dependencies
Use useLayoutEffect when you need synchronous DOM measurement
useEffect lets you synchronize a component with an external system — API calls, subscriptions, DOM events, timers.
Basic Usage
"use client"
import { useState, useEffect } from "react"
export default function Timer() {
const [seconds, setSeconds] = useState(0)
useEffect(() => {
const id = setInterval(() => {
setSeconds((s) => s + 1)
}, 1000)
// Cleanup — runs on unmount or before re-run
return () => clearInterval(id)
}, []) // Empty deps = runs once on mount
return <p>Elapsed: {seconds}s</p>
}
Dependency Array
| Deps | When effect runs |
|---|---|
[] | On mount, cleanup on unmount |
[a, b] | On mount + when a or b change |
| omitted | On every render (avoid if possible) |
Fetching Data
useEffect(() => {
let cancelled = false
fetch(`/api/users/${userId}`)
.then((r) => r.json())
.then((data) => {
if (!cancelled) setUser(data)
})
return () => {
cancelled = true
}
}, [userId])
Rules
- Call hooks at the top level — not inside loops, conditions, or nested functions
- Every
useEffectshould have a purpose — don't split without reason - If you don't need cleanup, you may not need
useEffect
Next Steps
- useReducer — Manage complex state
- State Management — Scale state beyond hooks
Key Takeaways
useEffect runs after the browser paints — use it for synchronizing with external systems (API calls, subscriptions, DOM)
The dependency array controls when the effect re-runs: empty [] runs once, omitted runs on every render, included deps run on change
The cleanup function (returned from the effect) prevents memory leaks by canceling subscriptions, timers, and listeners
Multiple useEffect calls separate concerns — don't combine unrelated side effects in a single effect
useLayoutEffect runs synchronously after DOM mutations but before the browser paints — use it for DOM measurements