useEffect Made Easy: A Beginner's Handbook


When building React appplications, managing side-effect is crucial - whether it’s fetching data, subscribing to a Websocket or updating the DOM manually. That's where React’s useEffect hook comes into play.
In this blog, we will dive into the useEffect hook, understand its syntax, how it works, how and when to use it, dependency array, cleanup function, side effects and so on. By the end, you will have a nice understanding of useEffect hook.
📌 What is useEffect?
useEffect in simple terms is:
A React hook used to handle side effects in functional component.
So, what is side effect then? Well, side effect is any logic that interacts with the outside world (outside the React component’s render cycle) or occurs after the component render.
Here are some examples:
Data fetching (APIs, Databases)
Manual DOM manipulation
Timers (setTimeout, setInterval)
Subscriptions (event listeners, Websocket connections)
Logging or analytics tracking
In React terms:
A side effect is something that runs after your component renders and affects or depends on things outside the component’s pure logic.
⚙️ Syntax
import { useEffect } from 'react'
useEffect(() => {
// Side effect code runs here (after render)
return () => {
// Cleanup code (runs before re-run or unmount)
};
}, [dependencies]); // Dependency array controls when the effect runs
🛠️ When does useEffect run?
The useEffect hook in React runs at specific times during a component's lifecycle, primarily after rendering and in response to dependency changes. Here’s a detailed breakdown of when it executes:
After every render (no dependency array)
useEffect(() => { // runs after every render (initial + updates) });
When: After the component mounts and after every re-render (initial and subsequent updates)
Use Case: Rarely needed. Avoid unless you have a specific reason.
Risk: Can cause infinite loops if the effect updates state unconditionally.
Only After Initial Render (Empty dependency Array)
useEffect(() => {
// Runs ONLY after the initial render (like `componentDidMount`)
}, []);
When: Once, when the component mounts (initial render)
Use Case: Initial setup (e.g., fetching data, setting up subscriptions).
Cleanup: Runs when the component unmounts (if a cleanup function is returned).
When dependencies change
useEffect(() => {
// Runs after initial render AND when `dep1` or `dep2` change
}, [dep1, dep2]);
When: Initial render and whenever any dependency in the array changes.
Use Case: Synchronizing with external data (e.g., re-fetching when userId changes).
Cleanup: Runs before the effect re-executes or on unmount.
Cleanup Phase
useEffect(() => {
// Side effect (e.g., subscribe to events)
return () => {
// Cleanup (e.g., unsubscribe)
};
}, [deps]);
When
Before re-running the effect
Before unmounting the component
Use Case: Preventing memory leaks (e.g., clearing timers, unsubscribing)
❌ Common mistakes to avoid
Missing dependencies
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
console.log(count); // Always logs 0 (stale closure)
}, 1000);
return () => clearInterval(timer);
}, []); // Missing `count` dependency
Problem: Count is stuck at 0 forever even if we update count with setCount.
How to fix ✅?
- Add count as dependency
useEffect(() => {
const timer = setInterval(() => {
console.log(count);
}, 1000);
return () => clearInterval(timer);
}, [count]);
Infinite Loop
const [value, setValue] = useState(0);
useEffect(() => {
setValue(value + 1); // Re-runs effect forever
}); // No dependency array
Problem: The effect updates state, triggering itself endlessly.
Fix: Add empty array to run once.
}, []); // ✅ Runs only on mount
Forgotten cleanup
useEffect(() => {
window.addEventListener('resize', handleResize);
// Missing cleanup ➜ crash if handleResize runs after unmount
}, []);
Problem: Not cleaning up subscriptions/timers can lead to memory leaks.
Fix: Always return a cleanup.
return () => window.removeEventListener('resize', handleResize); // ✅
🧩 Final Analogy: useEffect
as a Room Service
Imagine your React component is a hotel guest:
useEffect
is like room service that comes after you've checked in (render).If your room condition (state/props) changes, room service re-visits.
When you check out (unmount), they clean the room (cleanup function).
🧠 Conclusion
Understanding the useEffect hook is crucial for managing side effects in React applications. By knowing when and how it runs, you can efficiently handle tasks like data fetching and subscriptions.
Pay attention to the dependency array and include cleanup functions to prevent memory leaks. Avoid common mistakes such as missing dependencies and infinite loops to keep your components performant and reliable. With these insights, you're ready to effectively use useEffect in your projects.
Subscribe to my newsletter
Read articles from Raj Kiran Chaudhary directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Raj Kiran Chaudhary
Raj Kiran Chaudhary
Frontend developer who weaves creativity and code, book lover, avid traveller and a tech enthusiast