The useEffect
Hook
With the useEffect
hook, you can bind actions to lifecycle events inside function components.
In React, every component has a lifecycle! From when it is instantiated to when it is unmounted from the DOM. We can hook up to each stage by employing special methods and performing specific actions at different stages of each component's lifecycle.
Background: The lifecycle methods were introduced in the React Component
class. React would automatically call these methods at certain times during the "life" of a (class) Component. Here are the most commonly used ones (but there are more):
componentDidMount()
: invoked immediately after the component is inserted into the DOM.componentDidUpdate()
: invoked immediately after updating occurs. This method is not called for the initial render.componentWillUnmount()
: invoked immediately before a component is removed from the DOM.
The
useEffect
hook replaces all above three functions!
useEffect
as componentDidMount
In class component:
componentDidMount() {
this.fetchDataFromExternalAPI();
}
In function component:
useEffect(() => fetchDataFromExternalAPI(), []);
Notice the general syntax of useEffect
is:
useEffect(effectFunction, arrayDependencies);
If you want the useEffect
to behave as componentDidMount
, you must provide an empty array ([]
) as its dependency:
useEffect(() => {
console.log("Run only for first render, when component is mounted!");
}, []);
Unlike componentDidMount
, the useEffect
itself is not asynchronous. If you want to use async/await pattern, you should follow an approach similar to this:
useEffect(() => {
async function fetchData() {
const response = await axios(url);
console.log(response.data);
}
fetchData();
}, []);
useEffect
as componentWillUnmount
According to React's Docs, componentWillUnmount()
is invoked immediately before a component is unmounted and destroyed. Therefore, you should use it to perform any necessary cleanup in this method, such as canceling network requests or cleaning up any subscriptions that were created in componentDidMount()
.
componentDidMount() {
this.signInToChat();
}
componentWillUnmount() {
this.signOutOfChat();
}
In function component:
useEffect(() => {
signInToChat();
return () => signOutOfChat();
}, []);
Above, I'm returning a function from the "effect function." This returned function will be called to clean up the effect.
So, the useEffect
, given an empty dependency array, can replace two lifecycle methods!
useEffect(() => {
console.log("Component did mount!");
return () => {
console.log("Component will unmount!");
}
}, []);
useEffect
as componentDidUpdate
Recall that every change to state (or props) will cause a component to re-render. On every render, useEffect
will have a chance to run. That is, by default, your effects will execute on every render. Still, you can limit how often they run by providing the dependency array.
If you don't provide a dependency array:
useEffect(() => {
console.log("Component did update!");
console.log("I run on every re-render!");
});
Often, you'll only want an effect to run in response to a specific change. For example, maybe a prop's value changed or a change occurred to state. That's what the second argument of useEffect
is for: it's a list of "dependencies."
useEffect(() => {
console.log(`I only run when ${count} changes!`);
}, [count]);
This latter pattern is also helpful for debugging:
useEffect(() => {
console.log("Count is updated to ", count);
}, [count]);
const resetCount = () => setCount(0);
Resources
- React Docs, Using the Effect Hook
- The React Component Lifecycle.
- React's documentation on State and Lifecycle.