How to Use useEffect Hook in React
This article provides a straightforward guide on how to implement the
useEffect hook in React. You will learn its core syntax,
how to control its execution using the dependency array, and how to
write cleanup functions to prevent memory leaks. By the end of this
guide, you will be able to confidently handle side effects like data
fetching, subscriptions, and manual DOM updates in your React
components.
What is the useEffect Hook?
In React, the useEffect hook allows you to perform side
effects in functional components. Side effects are actions that happen
outside the scope of the normal component rendering process, such as
fetching data from an API, setting up event listeners, or interacting
directly with the browser DOM.
Basic Syntax
To use useEffect, you must import it from the React
library. The hook accepts two arguments: a callback function containing
the side effect logic, and an optional dependency array.
import { useEffect } from 'react';
useEffect(() => {
// Your side effect code goes here
}, [dependencies]);Controlling when useEffect Runs
The behavior of useEffect depends entirely on what you
pass into the second argument (the dependency array). There are three
common scenarios:
1. Running on Every Render (No Dependency Array)
If you omit the dependency array entirely, the effect will run after the initial render and after every subsequent update of the component.
useEffect(() => {
console.log('This runs on every single render');
});2. Running Only Once on Mount (Empty Dependency Array)
If you pass an empty array ([]), the effect runs exactly
once when the component is first mounted to the screen. This is ideal
for initial data fetching or setting up global event listeners.
useEffect(() => {
console.log('This runs only once when the component mounts');
}, []);3. Running When Specific Values Change (With Dependencies)
If you pass variables (props or state) into the dependency array, the effect will run on the initial mount and then rerun only when those specific variables change between renders.
const [count, setCount] = useState(0);
useEffect(() => {
console.log(`The count is now ${count}`);
}, [count]); // Only reruns when 'count' changesAdding a Cleanup Function
Some side effects require cleanup to avoid memory leaks, such as
clearing intervals, closing WebSocket connections, or removing event
listeners. To clean up an effect, return a function from your
useEffect callback. React will execute this cleanup
function before the component unmounts and before running the effect
again.
useEffect(() => {
const handleResize = () => {
console.log(window.innerWidth);
};
window.addEventListener('resize', handleResize);
// Cleanup function
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);Example: Fetching Data with useEffect
Here is a practical, real-world example of using
useEffect to fetch data from an API when a component
mounts:
import React, { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.json())
.then((data) => {
setUsers(data);
setLoading(false);
})
.catch((error) => {
console.error('Error fetching data:', error);
setLoading(false);
});
}, []); // Empty array ensures this only runs once
if (loading) return <p>Loading users...</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
export default UserList;