Introduction to the useEffect hook
The useEffect
hook is a function that allows you to perform side effects in function components. It is a hook that is called after every render of the component, including the first render. This makes it a great place to perform tasks that are related to the component, such as fetching data or updating the document title.
The useEffect
hook is called with a function as its argument. This function is called the "effect", and it is where you can perform your side effects. The effect can be synchronous or asynchronous, and it can return a clean-up function to be called when the component is unmounted.
Basic usage of the useEffect hook
import { useEffect } from 'react'
function Example() {
useEffect(() => {
// Perform some side effect
},[])
}
In the example above, the useEffect
hook is called with an empty array. This means that the side effect will be performed once only.
Adding dependencies to the useEffect hook
By default, the useEffect
hook is called after every render of the component. However, you can tell the useEffect
hook to only perform the side effect when certain values have changed by providing an array of dependencies as the second argument.
The dependencies are values that the effect depends on. If any of the dependencies change, the effect will be called again. If none of the dependencies change, the effect will not be called again.
Empty array of dependencies
import { useEffect } from 'react'
function Example() {
useEffect(() => {
// Perform some side effect
}, []) // Do not perform the side effect if any values change
}
In the example above, the side effect will only be performed once, on the first render of the component. It will not be performed again, even if the component re-renders.
This is useful for performing tasks that should only be done once, such as setting up an event listener or fetching data.
One dependency
import { useEffect, useState } from 'react'
function Example() {
const [count, setCount] = useState(0)
useEffect(() => {
// Perform some side effect
}, [count]) // Only perform the side effect if the count value changes
}
In the example above, the side effect will be performed every time the count
value changes. If any other values change, the side effect will not be performed.
This is useful for performing tasks that depend on a specific value, such as updating the document title or fetching data based on a search query.
No dependencies
import { useEffect } from 'react'
function Example() {
useEffect(() => {
// Perform some side effect
}) // Perform the side effect after every render
}
In the example above, the side effect will be performed after every render of the component. This includes the first render, as well as any subsequent re-renders.
This is useful for performing tasks that should be done after every render, such as updating the document title or fetching data.
Cleaning up after the useEffect hook
Sometimes, you may need to perform a clean up after the useEffect
hook has been called. For example, if you are setting up an event listener, you will need to remove the event listener when the component is unmounted. You can do this by returning a function from the useEffect
hook:
import { useEffect } from 'react'
function Example() {
useEffect(() => {
const handleClick = () => {
// Do something when the document is clicked
}
document.addEventListener('click', handleClick)
// Return a function to clean up after the useEffect hook
return () => {
document.removeEventListener('click', handleClick)
}
})
}
In the example above, the event listener is set up when the component is rendered, and it is cleaned up when the component is unmounted.
The clean-up function is called before the component is unmounted, and before the next effect is called. It is a good place to perform any clean-up tasks that are related to the effect, such as canceling network requests or removing event listeners.
Real-life use cases
Fetching data
import { useEffect, useState } from 'react'
function UserProfile({ userId }) {
const [user, setUser] = useState(null)
useEffect(() => {
// Fetch the user data when the component is rendered
async function fetchUser() {
const response = await fetch(`/api/users/${userId}`)
const data = await response.json()
setUser(data)
}
fetchUser()
}, [userId]) // Only fetch the user data if the userId changes
if (!user) {
return <p>Loading...</p>
}
return <p>{user.name}</p>
}
In the example above, we have a component that displays the name of a user. The component fetches the user data from an API when it is rendered, and it only fetches the data again if the userId
value changes. This is by far the most common use of useEffect.
Updating the document title
import { useEffect, useState } from 'react'
function Example() {
const [count, setCount] = useState(0)
useEffect(() => {
// Update the document title after every render
document.title = `You clicked ${count} times`
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
In the example above, the useEffect
hook updates the document title after every render of the component. The title reflects the current value of the count
state.
Adding and removing event listeners
import { useEffect } from 'react'
function Example() {
useEffect(() => {
const handleClick = () => {
// Do something when the document is clicked
}
document.addEventListener('click', handleClick)
// Return a function to clean up after the useEffect hook
return () => {
document.removeEventListener('click', handleClick)
}
})
}
In the example above, the useEffect
hook sets up an event listener when the component is rendered, and it removes the event listener when the component is unmounted.
Unusual Behaviour in React v18
A significant change that broke things was introduced in React 18: while Strict Mode is active, all components mount and unmount before being remounted again.
This means that each component is mounted, then unmounted, and then mounted again.
We can confirm the behaviour by using the cleanup function of the useEffect hook.
useEffect(() => {
console.log("Mounted");//Shown twice in output
return () => console.log("Cleanup");
}, []);
This only occurs in the development mode; it does not occur in the production mode. The only way to get rid of this is to disable StrictMode in our App, but it is not recommended to do so.
This was introduced so that in the future, when React decides to offer a feature that allows it to add or delete an area of the UI while still maintaining the state, this will help it do so. For instance, when moving between tabs, maintaining the state of the preceding tab might assist avoid the execution of effects such as API calls that are not essential.
You can read more about this at github.com/facebook/react/issues/24502
Conclusion and further readings
The useEffect
hook is a powerful tool for performing side effects in function components. It can be used to fetch data, update the document title, and set up or clean up event listeners. By providing an array of dependencies, you can control when the effect is called, and by returning a clean-up function, you can ensure that any necessary clean-up tasks are performed.
https://beta.reactjs.org/reference/react/useEffect
https://www.freecodecamp.org/news/react-useeffect-absolute-beginners/