JavaScript Curriculum
useEffect
mediumThe coffee shop menu needs to fetch items on load, update the page title when the category changes, and show a live order countdown timer. All three are side effects — things React doesn't handle automatically.
useEffect
useEffect is how you synchronise React components with the outside world — APIs, the DOM, timers, subscriptions. It runs after render, not during.
Four Firing Patterns
The dependency array controls everything:
Empty dependency array — runs once after the first render. Perfect for fetching initial data, setting up subscriptions, or reading from localStorage.
⚡ After first render onlyuseEffect(() => { // Runs ONCE after first render fetchMenuItems().then(setItems) document.title = 'The Grind — Menu' }, []) // ← empty array = mount only
Dependency Array Rules
Every dep mistake becomes a stale closure bug or an infinite loop:
Every value used inside the effect that comes from the component scope must be in the dependency array. Missing deps cause stale closure bugs.
// ❌ Missing 'userId' in deps — stale closure! useEffect(() => { fetchProfile(userId) // uses userId setTitle(`Profile: ${name}`) // uses name }, []) // neither listed — always uses initial values
// ✓ All used values listed useEffect(() => { fetchProfile(userId) setTitle(`Profile: ${name}`) }, [userId, name]) // re-runs when either changes
Cleanup Patterns — Four Real Scenarios
Timers, fetch abort, event listeners, and subscriptions:
Always clear timers when the component unmounts. Without cleanup, the callback fires on an unmounted component — causing a memory leak and possible state update errors.
useEffect(() => { const timerId = setInterval(() => { setSeconds(s => s + 1) }, 1000) // Cleanup: clear the timer when: // 1. Component unmounts // 2. Effect re-runs (deps changed) return () => clearInterval(timerId) }, []) // runs once, cleans up on unmount
// Click Start to see timer cleanup
Your Challenge
Write a useMenuItems(search) pattern inline: fetch /api/menu?q=${search} on mount and when search changes. Use AbortController — abort in cleanup. Show loading state while fetching. Add document.title = \Menu — ${search || 'All'}`in a separate effect. Add asetInterval` ticker that counts seconds since mount — clear it on unmount.
Challenge
Write a component that fetches menu items on mount (empty deps). Add a search term state — re-fetch when it changes using AbortController for cleanup. Sync document.title with the current category. Show a 30-second countdown that clears on unmount.
useEffect
mediumThe coffee shop menu needs to fetch items on load, update the page title when the category changes, and show a live order countdown timer. All three are side effects — things React doesn't handle automatically.
useEffect
useEffect is how you synchronise React components with the outside world — APIs, the DOM, timers, subscriptions. It runs after render, not during.
Four Firing Patterns
The dependency array controls everything:
Empty dependency array — runs once after the first render. Perfect for fetching initial data, setting up subscriptions, or reading from localStorage.
⚡ After first render onlyuseEffect(() => { // Runs ONCE after first render fetchMenuItems().then(setItems) document.title = 'The Grind — Menu' }, []) // ← empty array = mount only
Dependency Array Rules
Every dep mistake becomes a stale closure bug or an infinite loop:
Every value used inside the effect that comes from the component scope must be in the dependency array. Missing deps cause stale closure bugs.
// ❌ Missing 'userId' in deps — stale closure! useEffect(() => { fetchProfile(userId) // uses userId setTitle(`Profile: ${name}`) // uses name }, []) // neither listed — always uses initial values
// ✓ All used values listed useEffect(() => { fetchProfile(userId) setTitle(`Profile: ${name}`) }, [userId, name]) // re-runs when either changes
Cleanup Patterns — Four Real Scenarios
Timers, fetch abort, event listeners, and subscriptions:
Always clear timers when the component unmounts. Without cleanup, the callback fires on an unmounted component — causing a memory leak and possible state update errors.
useEffect(() => { const timerId = setInterval(() => { setSeconds(s => s + 1) }, 1000) // Cleanup: clear the timer when: // 1. Component unmounts // 2. Effect re-runs (deps changed) return () => clearInterval(timerId) }, []) // runs once, cleans up on unmount
// Click Start to see timer cleanup
Your Challenge
Write a useMenuItems(search) pattern inline: fetch /api/menu?q=${search} on mount and when search changes. Use AbortController — abort in cleanup. Show loading state while fetching. Add document.title = \Menu — ${search || 'All'}`in a separate effect. Add asetInterval` ticker that counts seconds since mount — clear it on unmount.
Challenge
Write a component that fetches menu items on mount (empty deps). Add a search term state — re-fetch when it changes using AbortController for cleanup. Sync document.title with the current category. Show a 30-second countdown that clears on unmount.