The useEffect Hook is one of the most powerful and important Hooks in React. It allows functional components to perform side effects, which are actions that occur outside the normal rendering process. Examples of side effects include API calls, event listeners, timers, logging, and manual DOM updates.
Before Hooks, side effects were handled using lifecycle methods in class components such as componentDidMount, componentDidUpdate, and componentWillUnmount. The useEffect Hook combines all these lifecycle behaviors into a single, flexible API.
Basic useEffect Syntax
import { useEffect } from "react";
useEffect(() => {
// side effect logic
});
By default, this effect runs after every render of the component.
Managing Side Effects with useEffect
Side effects should never be performed directly inside the component’s render logic because rendering must remain pure. The useEffect Hook ensures side effects run after React updates the UI.
Running an Effect Once (Component Did Mount)
To run an effect only once when the component mounts, pass an empty dependency array.
useEffect(() => {
console.log("Component mounted");
}, []);
This is commonly used for API calls or initialization logic.
Running an Effect When Data Changes
To run an effect when specific values change, include them in the dependency array.
useEffect(() => {
console.log("Count changed:", count);
}, [count]);
The effect runs only when count changes.
API Call Example
import { useState, useEffect } from "react";
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("/api/users")
.then(res => res.json())
.then(data => setUsers(data));
}, []);
return users.map(user => <p key={user.id}>{user.name}</p>);
}
This ensures the API call runs only once when the component loads.
Cleanup in useEffect
Some side effects require cleanup to avoid memory leaks or unwanted behavior. Cleanup is especially important for subscriptions, event listeners, timers, and intervals.
A cleanup function is returned from the useEffect callback and runs when the component unmounts or before the effect runs again.
Cleanup Example with Event Listener
useEffect(() => {
function handleResize() {
console.log("Window resized");
}
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
Here, the cleanup removes the event listener when the component unmounts.
Cleanup Example with setInterval
useEffect(() => {
const timer = setInterval(() => {
console.log("Running...");
}, 1000);
return () => clearInterval(timer);
}, []);
Without cleanup, the interval would continue running even after the component is removed.
Real-World Scenario
In real-world applications, useEffect is used for fetching user data on page load, subscribing to WebSocket updates, tracking user activity, or updating the document title. Cleanup ensures that when users navigate away or components unmount, resources are released properly and performance remains stable.
For example, a dashboard might subscribe to live data updates when mounted and unsubscribe when the user leaves the page.
Important Notes and Best Practices
Always specify dependencies correctly to avoid unnecessary re-renders or stale data. Never omit dependencies intentionally unless you fully understand the consequences. Use multiple useEffect Hooks to separate unrelated side effects instead of combining everything into one effect. Always clean up effects that create subscriptions, listeners, or timers.
In summary, the useEffect Hook is used to manage side effects in React functional components. Proper dependency management and cleanup are essential to ensure predictable behavior, prevent memory leaks, and build reliable, high-quality React applications.