Summary: in this tutorial, you will explore the React useEffect() hook, and learn how and when to use it properly.
Understanding React useEffect hook #
In React, side effects include data fetching, manual DOM manipulation, or any other operations that impact outside of the component.
To perform side effects in function components, you use the useEffect hook. To use the useEffect hook, you first import it from the react library:
import { useEffect } from 'react';Then, you can use it in your component with the following syntax:
useEffect(setup, dependencies)The useEffect hook is a function that takes two arguments:
setupis the main function containing side effects, which you want to execute.dependenciesis an optional array of dependent values that determine when thesetupfunction should run. TheuseEffecthook will run thesetupfunction based on theitemsspecified in thedependenciesarray.
The setup function can optionally return a cleanup function:
return cleanup;React executes the cleanup function before it unmounts the component or before the setup re-runs (except the first time).
In the cleanup function, you may include code for cleaning up resources such as clearing intervals or canceling a network request (AbortController).
Typically, you’ll write the useEffect hook as follows:
useEffect(() => {
// Side effect code here
return () => {
// Cleanup code here (optional)
};
}, [dependencies]);In this syntax, you define an arrow function for the setup function:
() => {
// Side effect code here
return () => {
// Cleanup code here (optional)
};
}And include values in the dependencies array:
[dependencies]Run an effect only once #
To run an effect ( or a function) once when the component mounts, you use an empty array as the dependencies array:
useEffect(() => {
// This code runs once
}, [])In practice, you use this pattern to run a function during the initial render of the component. For example, you can run a function that fetches data from an API only once when the component mounts.
The following UserList component displays a list of users by calling the API endpoint https://jsonplaceholder.typicode.com/users:
import React, { useEffect, useState } from 'react';
export default function UserList() {
const [users, setUsers] = useState([]);
const fetchUsers = async () => {
const url = 'https://jsonplaceholder.typicode.com/users';
const response = await fetch(url);
const data = await response.json();
setUsers(data);
};
useEffect(() => {
fetchUsers();
}, []);
return (
<div>
<h1>User List</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}Run an effect whenever dependencies change #
To run a function when certain values change, you can specify those values in the dependencies array.
The following CounterTitle component utilizes the useEffect() hook to update the document title when the count state variable changes:
import { useState, useEffect } from 'react';
const CounterTitle = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
<div>
<h1>Current count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default CounterTitle;Run an effect after every rerender #
To run a function during the initial render and after every rerender, you pass only the setup function to the useEffect() function and skip the dependencies array.
For example, you can log a message when the component renders and every time it re-renders by using the useEffect() function:
import React, { useState, useEffect } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log('Component rendered or rerendered');
});
return (
<div>
<h1>Current count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default Counter;Run a cleanup function #
The following WindowDimension component listens for window resize events and updates the width and height state variables based on the window’s size:
import { useState, useEffect } from 'react';
function WindowDimension() {
const [width, setWidth] = useState(window.innerWidth);
const [height, setHeight] = useState(window.innerHeight);
useEffect(() => {
// Define the handler function that updates the state with the window's width
const handleResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
};
// Add the event listener for window resize
window.addEventListener('resize', handleResize);
// Cleanup function: Remove the event listener when the component unmounts
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div>
<p>
Current dimension: {width}x{height}px
</p>
</div>
);
}
export default WindowDimension;How it works.
First, define handleResize() function to update the width and height state varibles based on the window’s innerWidth and innerHeight:
const handleResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
};Second, register an event listener for the resize event using the handleResize() function:
window.addEventListener('resize', handleResize);Third, return a cleanup function from the useEffect, which removes the resize event listener uisng window.removeEventListener method:
return () => {
window.removeEventListener('resize', handleResize);
};This prevents the component to call the handleResize function after it is removed from the DOM, which may lead to memory leak and unwanted behavior.
Summary #
- Use
useEffect(setup)to run thesetupfunction when the component renders and re-renders. - Use
useEffect(setup,[])to run thesetupfunction once during the initial render. - Use
useEffect(setup, [dependencies])to run thesetupfunction when the component renders and every time the items in thedependenciesarray change.
Thank you for your feedback!