useEffect Hook
What Are Side Effects?
Side effects are operations that affect the outside world or depend on it. Examples: fetching data, subscriptions, timers, DOM manipulation, and logging.
useEffect Basics
import React, { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
document.title = `Time: ${seconds} seconds`;
});
return <p>{seconds} seconds</p>;
}
Dependency Array
useEffect accepts a dependency array as its second parameter.
No Array (Every Render)
// Runs every time the component renders
useEffect(() => {
console.log('Render');
});
Empty Array (Mount Only)
// Runs once when the component mounts
useEffect(() => {
console.log('Component mounted');
}, []);
With Dependencies
// Runs when userId changes
useEffect(() => {
fetchUser(userId);
}, [userId]);
Cleanup Function
Return a function from useEffect for cleanup.
function ChatRoom({ roomId }) {
useEffect(() => {
const connection = connectToRoom(roomId);
return () => {
connection.disconnect(); // Cleanup
};
}, [roomId]);
return <p>Connected to {roomId}</p>;
}
Example: Fetching Data from an API
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
setLoading(true);
fetch(`/api/users/${userId}`)
.then(res => {
if (!res.ok) throw new Error('Failed to load');
return res.json();
})
.then(data => {
setUser(data);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, [userId]);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return <h2>{user.name}</h2>;
}
Timers and Intervals
function Stopwatch() {
const [time, setTime] = useState(0);
const [running, setRunning] = useState(false);
useEffect(() => {
if (!running) return;
const interval = setInterval(() => {
setTime(prev => prev + 1);
}, 1000);
return () => clearInterval(interval);
}, [running]);
return (
<div>
<p>{time} seconds</p>
<button onClick={() => setRunning(!running)}>
{running ? 'Stop' : 'Start'}
</button>
</div>
);
}
Event Listeners
function WindowSize() {
const [size, setSize] = useState({ width: 0, height: 0 });
useEffect(() => {
const handleResize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight });
};
handleResize();
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return <p>{size.width} x {size.height}</p>;
}
Summary
| Array Condition | Behavior |
|---|---|
| None | Every render |
[] |
Mount only |
[dep1, dep2] |
When a dependency changes |
useEffect is the main tool for side effects. Next we will study useContext to manage global data.