useEffect Hook & Side Effects
useEffect lets you perform side effects — fetching data, setting up subscriptions, or touching the DOM — after React has rendered. Understanding it well is essential for real-world apps.
1 useEffect Dependency Array
React — useEffect Patterns
import { useState, useEffect } from "react";
function Demo({ userId }) {
const [user, setUser] = useState(null);
// Runs EVERY render (no dependency array) — rarely needed
useEffect(() => { console.log("Rendered"); });
// Runs ONCE on mount (empty array)
useEffect(() => {
console.log("Component mounted");
return () => console.log("Component unmounted"); // cleanup
}, []);
// Runs when userId changes
useEffect(() => {
if (!userId) return;
let cancelled = false;
fetch(`/api/users/${userId}`)
.then(r => r.json())
.then(data => {
if (!cancelled) setUser(data); // avoid state update on unmounted
});
return () => { cancelled = true; }; // cleanup on userId change
}, [userId]); // dependency
return <div>{user ? user.name : "Loading..."}</div>;
}
2 Common useEffect Patterns
React — useEffect Examples
// Update document title
useEffect(() => {
document.title = `${count} new messages`;
}, [count]);
// Keyboard listener
useEffect(() => {
const handler = (e) => e.key === "Escape" && closeModal();
window.addEventListener("keydown", handler);
return () => window.removeEventListener("keydown", handler);
}, [closeModal]);
// Timer / interval
useEffect(() => {
const timer = setInterval(() => setTime(new Date()), 1000);
return () => clearInterval(timer); // cleanup stops the interval
}, []);
// Local storage sync
useEffect(() => {
localStorage.setItem("theme", theme);
}, [theme]);
3 Code Challenge
Challenge: Build a live search component that fetches results from
https://jsonplaceholder.typicode.com/users on mount, then filters them client-side using a search input. Add a debounce effect so filtering only triggers 300ms after the user stops typing.