React useEffect Patterns for Reliable Side Effects
🧠 useEffect Cheat Sheet (Beginner → Confident)
1️⃣ What is useEffect? (Plain English)
useEffect lets you run side effects in a React component.
👉 Side effects = things that are not UI rendering.
- Fetching data
- Calling APIs
- Using
localStorage - Timers (
setTimeout,setInterval) - Event listeners
- Updating the document title
Rule of thumb:
If it touches the outside world, it belongs inuseEffect.
2️⃣ Basic Syntax (Memorize This)
useEffect(() => {
// side effect code
}, [dependencies]);
📌 Two parts:
- Effect function → what to run
- Dependency array → when to run
3️⃣ The 3 Most Important Patterns (MUST KNOW)
✅ 1. Run once (on component mount)
useEffect(() => {
console.log("Component mounted");
}, []);
🧠 Think:
“Run this once when the component appears.”
📌 Common use cases:
- API calls
- Initial setup
- Fetching data
✅ 2. Run when something changes
useEffect(() => {
console.log("Count changed:", count);
}, [count]);
🧠 Think:
“Whenever
countchanges, run this.”
📌 Use cases:
- Re-fetching data
- Syncing state
- Updating the document title
❌ 3. Run on every render (rare & dangerous)
useEffect(() => {
console.log("Runs every render");
});
⚠️ Avoid unless you really know why — this runs after every render.
4️⃣ Real Examples (Very Important)
🔹 Change document title
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
✔ Runs only when count changes.
🔹 Fetching data from API
useEffect(() => {
fetch("https://api.example.com/users")
.then(res => res.json())
.then(data => setUsers(data));
}, []);
✔ Fetches once when the component mounts.
🔹 Using localStorage
useEffect(() => {
localStorage.setItem("name", name);
}, [name]);
✔ Syncs state with browser storage.
5️⃣ Cleanup Function (VERY IMPORTANT)
Some effects must be cleaned up or bugs and leaks can happen.
Syntax
useEffect(() => {
return () => {
// cleanup code
};
}, []);
🔹 Example: Event listener
useEffect(() => {
const handleResize = () => {
console.log(window.innerWidth);
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
🧠 Think:
“Clean up before the component unmounts.”
🔹 Example: setInterval
useEffect(() => {
const id = setInterval(() => {
console.log("Tick");
}, 1000);
return () => clearInterval(id);
}, []);
6️⃣ Dependency Array — How to Think
Rule
👉 Everything you use inside useEffect that comes from outside the effect should go in the dependency array.
Example
useEffect(() => {
console.log(name, age);
}, [name, age]);
❌ Wrong
useEffect(() => {
console.log(name);
}, []); // ❌ name used but not listed
This causes stale values.
7️⃣ Infinite Loop Problem (Common Beginner Mistake)
❌ Wrong
useEffect(() => {
setCount(count + 1);
}, [count]);
👉 The effect updates count → count triggers the effect again → 💥 infinite loop.
✅ Fix using functional update
useEffect(() => {
setCount(c => c + 1);
}, []);
8️⃣ Multiple useEffects (Best Practice)
Prefer multiple small, focused effects over one large effect.
// One big effect (less ideal)
useEffect(() => {
fetchData();
updateTitle();
addListener();
}, []);
// Better: multiple focused effects
useEffect(fetchData, []);
useEffect(updateTitle, [count]);
useEffect(addListener, []);
🧠 Cleaner and easier to debug.
9️⃣ When NOT to Use useEffect
❌ Don’t use useEffect for things that belong in render or derived values:
- Updating state directly from props
- Simple calculations
- Rendering logic
👉 Do this instead:
const total = price * quantity;
If it can be done during render — do not use useEffect.
🔟 Mental Model (Remember This Forever)
Render = describe UI
useEffect = sync with outside world
🔥 Common Mistakes Checklist
- ❌ Forgetting the dependency array
- ❌ Updating state without cleanup
- ❌ Using
useEffectfor simple calculations - ❌ Causing infinite loops
- ❌ Ignoring cleanup functions
🧪 Tiny Practice Snippet
useEffect(() => {
console.log("Hello React");
}, []);
Ask yourself:
- When does it run? ✅ once
- Why? ✅ empty dependency array
