To carry out side effects, for example, working directly with the DOM in React, we use the built-in useEffect()
. It replaces three life cycle callbacks:
componentDidMount()
componentDidUpdate()
componentWillUnmount()
If you forgot or didn't know how they work, you can read more about them in the official documentation.
Let's start with a simple example:
import React, { useState, useEffect } from 'react';
const Example = () => {
const [count, setCount] = useState(0);
// Works like componentDidMount and componentDidUpdate combined
// Starts after rendering the component
// Called after each click on the button
useEffect(() => {
// The state is accessible internally, as the scope is normal
alert(`Clicks ${count}`);
});
// With classes, we'd do this
// Note the duplication
// componentDidMount() {
// alert(`Clicks: ${count}`);
// }
// componentDidUpdate() {
// alert(`Clicks: ${count}`);
// }
return (
<div>
<p>You clicked {count} time(s)</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
};
Below is an example in which the background changes with each click:
See the Pen js_react_hooks_use_effect-1 by Hexlet (@hexlet) on CodePen.
The callback passed to useEffect()
is processed after the component's first rendering and after each update. In other words, we combined the componentDidUpdate()
and componentDidMount()
methods for convenience.
The practice using React has shown that effects occur after each render, regardless of whether this is the first or subsequent rendering. We reduced the amount of duplication in our code as a bonus.
Let us observe some typical side effects found in the front end:
- Data extraction
- Working with the BOM API (Browser Object Model), for example, Local Storage
- Direct DOM modification, this also includes libraries that are incompatible with React
Sometimes we can skip the action of the useEffect()
hook. It can be helpful for optimization purposes or cases when we use the effect only under certain conditions.
To do this, we should track an array of values between renderings passed to the hook as the second argument. We trigger the callback if we change at least one value from this array.
If all values remain the same, we skip it:
useEffect(() => {
alert(`Clicks ${count}`);
}, [count]);
// Equivalent
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
alert(`Clicks ${count}`);
}
}
In other words, we trigger the callback only when the count
changes.
In the same way, you can pass any set of variables to associate them with the effect change. The effect will work when at least one variable in the passed array has changed. Otherwise, React will skip it.
How can we run useEffect()
only when the first render occurs (immediately after mounting)?
To do so, you need to pass an empty array:
// Replaces componentDidMount
useEffect(() => {
alert(`Clicks ${count}`);
}, []);
This solution isn't the most obvious, but technically, it is not a specific case. Someday you will get used to it.
Resetting the effect
Sometimes we need to reset. For instance, we should clean the effect up when it ceases to be relevant after changing the props.
To do this, you can return the function from useEffect()
to move the cleanup inside:
// Let's assume that this effect depends on the userId prop
useEffect(() => {
const id = setTimeout(/* some code with userId */);
return () => clearTimeout(id);
}, [userId]);
Changing the userId
will reset the current timer and install a new one. This code would require as many as four life cycle callbacks in classes.
We can connect the cleanup to an empty array with a second parameter to simulate componentWillUnmount()
:
useEffect(() => {
return () => {
// This logic executes only when the component is unmounted
};
}, []);
Recommended materials
Are there any more questions? Ask them in the Discussion section.
The Hexlet support team or other students will answer you.
For full access to the course you need a professional subscription.
A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.