useEffect Hook — Side Effects in React
DodaTech
3 min read
In this tutorial, you'll learn about useeffect hook. We cover key concepts, practical examples, and best practices.
What You'll Learn
Master React's useEffect hook — run side effects in function components, fetch API data, subscribe to events, clean up resources, and manage dependencies.
Why It Matters
Side effects are what make apps useful — loading data, updating the DOM, setting up subscriptions. useEffect controls when and how these happen.
Real-World Use
Fetching user data when a profile page loads, updating the document title, subscribing to a WebSocket, or starting/stopping a timer.
Basic useEffect
import { useState, useEffect } from "react";
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
// Cleanup function — runs when component unmounts
return () => clearInterval(interval);
}, []); // Empty array = run once on mount
return <p>Seconds: {seconds}</p>;
}
Data Fetching
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
async function fetchUsers() {
try {
setLoading(true);
setError(null);
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
if (!cancelled) {
setUsers(data);
}
} catch (err) {
if (!cancelled) {
setError(err.message);
}
} finally {
if (!cancelled) {
setLoading(false);
}
}
}
fetchUsers();
// Cleanup to avoid state updates on unmounted component
return () => {
cancelled = true;
};
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Dependency Array Patterns
// 1. Runs after EVERY render (rarely used)
useEffect(() => {
console.log("Rendered!");
});
// 2. Runs ONCE after mount (data fetching, subscriptions)
useEffect(() => {
fetchData();
}, []);
// 3. Runs when specific values change
useEffect(() => {
document.title = `User: ${username}`;
}, [username]);
// 4. Runs when any dependency changes
useEffect(() => {
fetchUserPosts(userId);
}, [userId, page]);
The Dependency Array
If you use a variable inside useEffect, include it in the dependency array:
function ProductPage({ productId }) {
const [product, setProduct] = useState(null);
// ❌ Missing dependency
useEffect(() => {
fetch(`/api/products/${productId}`)
.then(r => r.json())
.then(setProduct);
}, []); // Bug: uses productId but doesn't list it
// ✅ Correct
useEffect(() => {
fetch(`/api/products/${productId}`)
.then(r => r.json())
.then(setProduct);
}, [productId]);
}
Cleanup Functions
Cleanup prevents memory leaks and stale effects:
function ChatRoom({ roomId }) {
useEffect(() => {
// Setup
const connection = createConnection(roomId);
connection.connect();
// Cleanup — runs when:
// 1. Component unmounts
// 2. Before re-running the effect (dependencies changed)
return () => {
connection.disconnect();
};
}, [roomId]);
}
Event Listeners
function WindowSize() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
function handleResize() {
setWidth(window.innerWidth);
}
window.addEventListener("resize", handleResize);
// Cleanup
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return <p>Window width: {width}px</p>;
}
Common Mistakes
| Mistake | Fix |
|---|---|
| Missing dependency | Add all used variables to array |
| Infinite loop | Don't update state that's a dependency |
| No cleanup | Always clean up subscriptions |
| Async effect function | Create async function inside effect |
| Stale closure | Use functional state update |
| Fetch → setState after unmount | Use cancel flag (like cancelled) |
Practice
// 1. Fetch data from an API and display it
// 2. Create a timer with start/stop
// 3. Track mouse position with event listener
// 4. Sync document title with a state variable
// 5. Subscribe to a WebSocket and clean up
← Previous
useState Hook — Managing State in React
Next →
React Context API — Global State Management
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro