React useEffect Hook
Managing side effects in functional components
โก What is useEffect?
useEffect is a React Hook that lets you perform side effects in functional components. It handles operations like data fetching, subscriptions, timers, and DOM manipulation after rendering completes.
import { useEffect } from 'react';
function Example() {
useEffect(() => {
document.title = 'Hello World';
});
}
useEffect Key Concepts
Side Effects
Run code after render
useEffect(() => {
// Effect code
});
Dependencies
Control when effect runs
useEffect(() => {
// Effect
}, [dependency]);
Cleanup
Clean up resources
useEffect(() => {
return () => {
// Cleanup
};
});
Run Once
Execute on mount only
useEffect(() => {
// Runs once
}, []);
๐น Basic useEffect Example
The simplest useEffect runs after every render. This example updates the document title whenever the component renders, showing how effects execute after the DOM updates.
import { useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// Runs after every render
useEffect(() => {
document.title = `Count: ${count}`;
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
</div>
);
}
Output:
Count: 0
Document title updates with count
๐น useEffect with Dependencies
The dependency array controls when the effect runs. By passing specific values, the effect only executes when those values change, optimizing performance and preventing unnecessary operations.
import { useState, useEffect } from 'react';
function SearchUser() {
const [userId, setUserId] = useState(1);
const [user, setUser] = useState(null);
// Runs only when userId changes
useEffect(() => {
fetch(`https://api.example.com/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]); // Dependency array
return (
<div>
<button onClick={() => setUserId(userId + 1)}>
Next User
</button>
<p>User ID: {userId}</p>
</div>
);
}
Output:
User ID: 1
Fetches new user when ID changes
๐น useEffect Run Once (Empty Dependencies)
An empty dependency array makes the effect run only once when the component mounts. This is perfect for initial data fetching, setting up subscriptions, or one-time setup operations.
import { useState, useEffect } from 'react';
function UserProfile() {
const [user, setUser] = useState(null);
// Runs only once on mount
useEffect(() => {
fetch('https://api.example.com/user')
.then(res => res.json())
.then(data => setUser(data));
}, []); // Empty array = run once
return (
<div>
{user ? (
<h2>Welcome, {user.name}!</h2>
) : (
<p>Loading...</p>
)}
</div>
);
}
Output:
Loading...
Fetches data once when component loads
๐น useEffect with Cleanup
Return a cleanup function from useEffect to handle resource cleanup. This is essential for subscriptions, timers, and event listeners to prevent memory leaks when the component unmounts.
import { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
// Set up interval
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
// Cleanup function
return () => {
clearInterval(interval);
console.log('Timer cleaned up');
};
}, []); // Run once on mount
return (
<div>
<p>Timer: {seconds} seconds</p>
</div>
);
}
Output:
Timer: 0 seconds
Timer increments every second, cleans up on unmount
๐น Multiple useEffect Hooks
You can use multiple useEffect hooks in one component to separate different concerns. Each effect can have its own dependencies and cleanup logic, making code more organized and maintainable.
import { useState, useEffect } from 'react';
function Dashboard() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
// Effect 1: Fetch user data
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data));
}, []);
// Effect 2: Fetch posts
useEffect(() => {
fetch('/api/posts')
.then(res => res.json())
.then(data => setPosts(data));
}, []);
// Effect 3: Update document title
useEffect(() => {
if (user) {
document.title = `${user.name}'s Dashboard`;
}
}, [user]);
return <div>Dashboard Content</div>;
}
Output:
Dashboard Content
Three separate effects handle different tasks
๐น Common useEffect Patterns
๐ธ Data Fetching
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => setData(data));
}, []);
๐ธ Event Listeners
useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
๐ธ Subscriptions
useEffect(() => {
const subscription = dataSource.subscribe();
return () => subscription.unsubscribe();
}, []);
๐น useEffect Best Practices
โ Do's:
- Always specify dependencies: Include all values used in effect
- Clean up side effects: Return cleanup function when needed
- Separate concerns: Use multiple effects for different tasks
- Handle async properly: Don't make useEffect callback async
โ Don'ts:
- Don't omit dependencies: Always include what you use
- Don't mutate state directly: Use setter functions
- Don't forget cleanup: Clear timers, subscriptions, listeners
- Don't overuse: Not all logic needs useEffect