The useEffect
Hook
With the useEffect
hook, you can bind actions to lifecycle events inside function components. Namely, three of them: componentDidMount
, componentDidUpdate
, and componentWillUnmount
. All with one function!
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!
In class component:
resetCount = () => {
this.setState(
{ count: 0 },
() => console.log("Count is updated to ", count);
)
}
In function component:
useEffect(() => {
console.log("Count is updated to ", count);
}, [count]);
const resetCount = () => setCount(0);
Notice you cannot provide a second call back function argument to setCount
. This is a departure from how this.setState
works in class components.
Resources
- React Docs, Using the Effect Hook