The useEffect hook is a fundamental React hook that enables functional components to perform side effects. It replaces lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount from class components.
This guide covers:
useEffect
is and what side effects areuseEffect
The useEffect
hook lets you synchronize a component with external systems or perform actions that aren't directly tied to rendering, such as fetching data or updating the DOM.
A side effect is any operation that affects something outside the scope of the component's render function. Examples include:
setTimeout
, setInterval
)graph TD
A[Component Renders] --> B{Render Logic}
B --> C[Output UI]
B --> D[useEffect]
D --> E[Side Effects]
E --> F[External Systems]
F --> |Updates| A
📖 Read more: Synchronizing with Effects
Use useEffect
when your component needs to handle side effects at specific moments in its lifecycle.
Run code once after the component is added to the DOM (similar to componentDidMount
).
useEffect(() => {
// Fetch data when component mounts
fetch('https://api.example.com/data')
.then(res => res.json())
.then(data => console.log(data));
}, []); // Empty array = run once on mount
Run code whenever specific values (dependencies) change (similar to componentDidUpdate
).
useEffect(() => {
document.title = `User: ${userId}`;
}, [userId]); // Runs when userId changes
Run cleanup code before the component unmounts or before the effect runs again (similar to componentWillUnmount
).
useEffect(() => {
const timer = setInterval(() => console.log('Tick'), 1000);
// Cleanup: runs on unmount or before next effect
return () => clearInterval(timer);
}, []); // Empty array = cleanup on unmount
graph TD
A[Component Mounts] --> B[Run useEffect]
B --> C[Dependency Changes?]
C -->|Yes| D[Cleanup Old Effect]
D --> B
C -->|No| E[Do Nothing]
A --> F[Component Unmounts]
F --> G[Run Cleanup]
🎬 Watch: React useEffect Hook Explained
The useEffect
hook takes two arguments:
useEffect(() => {
// Effect code here
console.log('Effect ran');
// Optional cleanup function
return () => {
console.log('Cleanup ran');
};
}, [dependency1, dependency2]); // Dependency array
[]
): Effect runs once on mount, cleanup on unmount.📖 Read more: useEffect Reference
The dependency array is an optional second argument to useEffect
that lists variables the effect depends on. It controls when the effect (and its cleanup) runs.
graph LR
A[Render] --> B{Dependency Array}
B -->|Empty| C[Run Once on Mount]
B -->|No Array| D[Run Every Render]
B -->|Has Values| E{Values Changed?}
E -->|Yes| F[Run Effect]
E -->|No| G[Skip Effect]
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`Count is now ${count}`);
}, [count]); // Only runs when count changes
📖 Read more: How the Dependency Array Works
Forgetting to include dependencies or updating a dependency inside the effect can cause infinite re-renders.
// ❌ Bad: Infinite loop
const [data, setData] = useState(null);
useEffect(() => {
setData(fetchData()); // Updates state every render
}, []); // Missing data dependency
// ✅ Good: Include all dependencies
useEffect(() => {
setData(fetchData());
}, [fetchData]); // Include function if it changes
Not cleaning up subscriptions or intervals can lead to memory leaks.
// ❌ Bad: No cleanup
useEffect(() => {
const id = setInterval(() => console.log('Tick'), 1000);
}, []);
// ✅ Good: Cleanup
useEffect(() => {
const id = setInterval(() => console.log('Tick'), 1000);
return () => clearInterval(id);
}, []);
Avoid using useEffect
for logic that could be handled in event handlers or render logic.
// ❌ Overcomplicated
const [value, setValue] = useState(0);
useEffect(() => {
setValue(Math.random());
}, []);
// ✅ Simpler: Use initial state
const [value, setValue] = useState(Math.random());
📖 Read more: You Might Not Need an Effect