Understanding how useEffect works: A comprehensive guide
Outline
· Introduction
∘ What is useEffect?
∘ React lifecycle and useEffect
· How useEffect works
∘ Code Structure
· When to use useEffect
∘ When performing side effects
∘ When updating state
∘ When cleaning up after component
∘ When controlling the component rendering
· Conclusion
Hooks are important in React functional components and can come in handy when building React applications. There are a number of hooks in React but one of the most popular hooks that is used in many React applications is the useEffect
hook. In this article, we’ll look at how the useEffect
hook works.
Introduction
What is useEffect?
useEffect
is a hook in React functional components that allows you to perform side effect functions. Side effect functions are actions that affect the states or behaviour of the component or outside the scope of the component, like when fetching data from an API, using event handlers, and modifying the DOM among others.
React lifecycle and useEffect
If you’re transitioning from class-based components to function-based components in React, you can think of useEffect
as an equivalent to ComponentDidMount
, ComponentDidUpdate
, and ComponentWillUnmount
methods.
In useEffect
, there are three phases in React lifecycle, from initiation to destruction:
i. Mounting: When a component loads for the first time or is reloaded, it is said to have mounted. The component is initialized and inserted into the DOM and its state is set. This phase is similar to the ComponentDidMount method in class-based components.
ii. Updating: This is the next phase in the lifecycle, equivalent to ComponentDidUpdate. The component updates when there is a change in the state or props.
iii. Unmounting: In this final phase, the component’s state is destroyed through a cleanup function. A cleanup function is used to remove the component from the DOM, similar to ComponentWillUnmount in class-based components.
How useEffect works
Code Structure
Below this the structure of a typical useEffect
function:
import React, { useEffect } from 'react';
function Component() {
useEffect(() => {
// Side effect code here
// This code will run after every render
// Optional cleanup function
return () => {
// Code to clean up after the effect
// This code will run before the next effect runs
};
}, [/* dependency array */]);
// Component code here
// This code will run after the side effect code
return (
// JSX to render the component
);
}
Usually, useEffect
takes two arguments: a function and an array.
The first argument is a callback function that runs after the component renders. This function runs every time the component re-renders, depending on the second argument, the array. The cleanup function is optional and is only needed if the useEffect
function is unmounting and requires cleanup.
The second argument, the dependencies array, controls the re-rendering of the component. You can add states and props to this array to control the component’s rendering. If you leave the array as an empty array, the component will only render once. Not including at least an empty array can cause the component to mount infinitely, which is bad. If you include one or more dependencies in the array, useEffect
will only run if any of the dependencies change.
When to use useEffect
When performing side effects
Side effect functions that affect the component or the outside world state or behaviour are done in useEffect
functions. These functions such as fetching data from an API, setting a timer, manipulating the DOM, etc, can only happen after the component is rendered.
import { useState, useEffect } from 'react';
function Component() {
const [data, setData] = useState([]);
useEffect(() => {
fetch('<https://api.apiwebsitename.com/>')
.then(response => response.json())
.then(data => setData(data))
.catch(error => console.error(error));
}, []);
return (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
This code snippet shows data being fetched from an API only once due to the empty dependency array. Initially, the data state was an empty array, but after the side effect function in the useEffect
, the state gets updated.
When updating state
A change in a state can be used to trigger the update of another state with the help of useEffect
.
import { useState, useEffect } from 'react';
function Component() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count is currently at ${count}`;
}, [count]);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
In the above example, the document title gets updated with every click event, incrementing the count state and triggering the useEffect
with each change in the count state.
When cleaning up after component
For example, when setting a timer or an interval, removing an event listener, or canceling a subscription, cleaning up with useEffect
is important to prevent memory leaks that can cause issues in the application.
import { useEffect } from 'react';
function Component() {
useEffect(() => {
const timer = setTimeout(() => {
console.log('Timeout completed.');
}, 3000);
return () => {
clearTimeout(timer);
console.log('Timeout cleared.');
};
}, []);
...
In the provided example, the timeout function is called after 3 seconds, after which it is cleared out of the memory when the component unmounts.
When controlling the component rendering
Unlike when a state is updated, this, that is when controlling the component state, determines whether or not the component should be rendered at all. States used return boolean value while the states used when updating ranges from strings to objects.
import React, { useState, useEffect } from "react";
function Component(props) {
const [isLoading, setIsLoading] = useState(true);
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const result = await fetch("<https://api.apiwebsitename.com/>");
const json = await result.json();
setData(json);
setIsLoading(false);
}
fetchData();
}, [props.userId]);
return (
<div>
{isLoading ? (
<div>Loading...</div>
) : (
<>
<h1>{data.name}</h1>
<p>Your email is {data.email}.</p>
</>
)}
</div>
);
}
A common example of this is when a user profile is being fetched. If there is a userId or the userId exists, the component displays, if not, the component doesn’t.
Conclusion
Although useEffect
can be challenging for beginners, but with practice, it becomes easier to understand and use. By understanding how useEffect
works, you can effectively execute your React application, enhancing its behaviour and function.
Overall, useEffect
is an important hook and should be harnessed to optimise the functionality and performance of your React components.