💡 Key Takeaways
⚛️ useEffect: Manage side effects like data fetching, DOM updates, timers, and subscriptions.
🔹 Dependencies: Controls when the effect runs ([] for mount-only, list for selective updates).
⚙️ Cleanup: Return a function to remove listeners or timers, avoiding memory leaks.
📡 Async & Fetching: Wrap async logic inside effects; handle errors properly.
🔹 Patterns: Conditional effects and multiple dependencies for dynamic behavior.
⚛️ What is useEffect
- Handles side effects in functional components
- Replaces lifecycle methods (
componentDidMount,componentWillUnmount) - Supports dependency arrays for selective updates
- Allows cleanup to prevent memory leaks
import { useEffect } from 'react';
🔹 Basic Usage
- Runs after every render by default
- No dependencies triggers on all updates
- Avoid blocking operations inside the effect
useEffect(() => {
console.log("Effect runs after every render");
});
✅ Run Once on Mount
- Pass empty array
[]for mount-only behavior - Useful for initial data fetches or setup tasks
- Equivalent to
componentDidMount
useEffect(() => {
console.log("Component mounted");
}, []);
🔄 Run on Dependency Change
- Trigger effect on mount and when listed dependencies change
- Keeps state and effects in sync
- List only necessary dependencies to avoid extra renders
useEffect(() => {
console.log(`Count is now ${count}`);
}, [count]);
⚙️ Cleanup Function
- Return a function for cleanup
- Runs before unmount or next effect
- Ideal for timers, subscriptions, and event listeners
useEffect(() => {
const timer = setInterval(() => console.log("tick"), 1000);
return () => {
clearInterval(timer);
console.log("Cleaned up");
};
}, []);
📡 Fetching Data
- Wrap async calls inside the effect
- Run once on mount or when dependencies change
- Update state with fetched data
- Handle errors to prevent unhandled rejections
useEffect(() => {
async function fetchData() {
const res = await fetch("/api/data");
const json = await res.json();
setData(json);
}
fetchData();
}, []);
🔹 Common Patterns
Conditional Effect
- Run only if conditions are met
- Avoid unnecessary operations
- Useful for authenticated users or optional props
useEffect(() => {
if (!user) return;
console.log("User exists:", user);
}, [user]);
Watching Multiple Dependencies
- Trigger when any dependency changes
- Keep dependencies minimal to prevent extra renders
- Useful for combined state updates or form validation
useEffect(() => {
console.log("Either a or b changed:", a, b);
}, [a, b]);