React useEffect Hook: Beginner’s Guide with Examples
In React, the useEffect Hook allows you to perform side effects in functional components.
Side effects are operations that interact with the outside world or are not purely about rendering:
Fetching data from APIs
Subscribing to events
Updating the document title
Setting timers or intervals
Before Hooks, these were only possible in class components using componentDidMount, componentDidUpdate, and componentWillUnmount.
useEffect combines all of these lifecycle methods into a single Hook.
Syntax
useEffect(() => {
// Code to run (side effect)
return () => {
// Cleanup code (optional)
};
}, [dependencies]);
First argument: function containing your side effect
Return function: optional cleanup (runs on unmount or before next effect)
Dependencies array: controls when the effect runs
Example 1: useEffect on Component Mount
import { useEffect } from "react";
function App() {
useEffect(() => {
console.log("Component mounted!");
}, []); // Empty dependency array → runs once on mount
return <h1>Hello, useEffect!</h1>;
}
[] means the effect runs only once, similar to componentDidMount in class components.
Example 2: useEffect on State Change
import { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`Count updated: ${count}`);
}, [count]); // Runs whenever 'count' changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increase</button>
</div>
);
}
The effect runs every time the dependency (count) changes.
Example 3: useEffect Without Dependencies
useEffect(() => { console.log("Effect runs on every render"); }); Without a dependency array, the effect runs after every render.
Useful for debugging or continuous side effects, but can affect performance.
Example 4: Cleanup with useEffect
import { useEffect, useState } from "react";
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCount(prevCount => prevCount + 1);
}, 1000);
// Cleanup function
return () => clearInterval(interval);
}, []); // Empty array → setup once
return <h1>Timer: {count}</h1>;
}
The cleanup function runs when the component is unmounted.
Prevents memory leaks (important for timers, subscriptions, or event listeners).
Example 5: Updating Document Title
import { useState, useEffect } from "react";
function TitleUpdater() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Clicked ${count} times`;
}, [count]); // Runs whenever count changes
return <button onClick={() => setCount(count + 1)}>Click Me</button>;
}
Shows a real-world use case of useEffect.
Best Practices for useEffect
| Practice | Explanation |
|---|---|
| Use dependencies wisely | List all variables the effect depends on to avoid bugs. |
| Cleanup side effects | Prevent memory leaks with return () => { ... }. |
| Avoid heavy computations inside effect | Keep it fast; move heavy logic outside if possible. |
| Multiple effects | Split into multiple useEffect calls instead of combining unrelated effects. |
FAQ
Q1: What is a side effect?
Any operation outside rendering, e.g., API calls, timers, DOM manipulation.
Q2: Can I have multiple useEffect Hooks?
Yes! Each can handle a separate effect for better code organization.
Q3: What happens if I don’t pass a dependency array?
The effect runs after every render, which may hurt performance.