The useEffect hook is a very useful and powerful tool, especially when used properly. Understanding how to use it was a bit difficult for me at first, so that's the purpose of me writing this article. So Let's get started then!
useEffect is basically for handling side effects in your functional component. But what are side effects? Side effects are basically anything that affects something outside of the scope of the current function that's being executed. A very typical example of side effects are API requests.
useEffect hooks are used in place of lifecycle methods componentDidMount, componentDidUpdate and componentWillUnmount.
useEffects takes in two arguments:
useEffect(callback, [dependencies]);callback is the callback function containing the side-effect logic. useEffect executes the callback function after React has committed the changes to the screen. While dependencies is an optional array of dependencies. useEffect executes callback only if the dependencies have changed between renderings.
import { useEffect } from 'react';
const Example = () => {
useEffect(() => {
console.log('render [Example]'); //Runs after every render
});
return <div />;
};import { useEffect } from 'react';
function Example() {
useEffect(() => {
console.log('render [Example]'); //Runs once after initial render
}, []);
}import { useEffect } from 'react';
function Example() {
useEffect(() => {
//Runs once after initial render
// and after every rendering ONLY IF `prop` or `state` changes
console.log('render [Example]');
}, [prop, state]);
}To do this, all you have to do is use an empty array dependency:
import { useEffect } from 'react';
function Greet({ name }) {
const message = `Hello, ${name}!`;
useEffect(() => {
// Runs once, after mounting
document.title = 'Greetings page';
}, []);
return <div>{message}</div>;
}
useEffect(..., []) was supplied with an empty array as a dependencies argument. When configured in such a way, the useEffect() is going to execute the callback just once, after initial mounting.
Each time the side-effect uses props or state values, you must indicate these values as dependencies:
import { useEffect } from 'react';
function MyComponent({ prop }) {
const [state, setState] = useState();
useEffect(() => {
// Side-effect uses `prop` and `state`
}, [prop, state]);
return <div>....</div>;
}The useEffect(callback, [prop, state]) invokes the callback after the changes are being committed to DOM and if and only if any value in the dependencies array [prop, state] has changed.
Using the dependencies argument of useEffect() you control when to invoke the side-effect, independently from the rendering cycles of the component. Again, that’s the essence of useEffect() hook.
Let’s improve the Greet component by using name prop in the document title:
import { useEffect } from 'react';
function Greet({ name }) {
const message = `Hello, ${name}!`;
useEffect(() => {
document.title = `Greetings to ${name}`;
}, [name]);
return <div>{message}</div>;
}name prop is mentioned in the dependencies argument of useEffect(..., [name]). useEffect() hook runs the side-effect after initial rendering, and on later renderings only if the name value changes.
// First render
<Greet name="Eric" /> // Side-effect RUNS
// Second render, name prop changes
<Greet name="Stan" /> // Side-effect RUNS
// Third render, name prop doesn't change
<Greet name="Stan" /> // Side-effect does NOT RUN
// Fourth render, name prop changes
<Greet name="Butters"/> // Side-effect RUNSuseEffect() can perform data fetching side-effect.
The following component FetchEmployeesByQuery fetches the employees list over the network. The query prop filters the fetched employees:
import { useEffect, useState } from 'react';
function FetchEmployeesByQuery({ query }) {
const [employees, setEmployees] = useState([]);
useEffect(() => {
async function fetchEmployees() {
const response = await fetch(
`/employees?q=${encodeURIComponent(query)}`
);
const fetchedEmployees = await response.json(response);
setEmployees(fetchedEmployees);
}
fetchEmployees();
}, [query]);
return (
<div>
{employees.map(name => <div>{name}</div>)}
</div>
);
}useEffect() starts a fetch request by calling fetchEmployees() async function after the initial mounting.
When the request completes, setEmployees(fetchedEmployees) updates the employees state with the just fetched employees list.
On later renderings, if the query prop changes, useEffect() hook starts a new fetch request for a new query value.
Note that the callback argument of useEffect(callback) cannot be an async function. But you can always define and then invoke an async function inside the callback itself:
function FetchEmployeesByQuery({ query }) {
const [employees, setEmployees] = useState([]);
useEffect(() => { // <--- CANNOT be an async function
async function fetchEmployees() {
// ...
}
fetchEmployees(); // <--- But CAN invoke async functions
}, [query]);
// ...
}To run the fetch request once when the component mounts, simply indicate an empty dependencies list: useEffect(fetchSideEffect, [])
useEffect hook is a mechanism for making side effects in functional components. Side effects should not be caused directly in components body or render function, but should always be wrapped in a callback passed to useEffect.useEffect callback (and corresponding cleanup) is ran on initial render and every rerender as well as on dismount. If you want to change that behaviour, add an array of values as a second argument to the useEffect. Then the effects will be ran only on mount and unmount of the component or if the values in that array changed. If you want to trigger the effects only on mount and unmount, simply pass an empty array.