As the name suggests, it is adding side-effects to the functional components via useEffect Hook.
In simple sense, if you are from a class based component background: It is similar to saying that adding life cycle methods to your functional component.
As in class based component, We have these life cycle methods:
- ComponentDidMount
- ComponentDidUpdate
- ComponentWillUnmount
So useEffect() is a combination of all the above lifecycle methods combined into one.
What are the use cases of useEffect() ?
- Fetching data via api’s
- Update something on component render
- To make changes to the DOM
Lets discuss about it with an example:
Example: EffectHooks.js
import React, { useEffect, useState } from "react";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
import Button from "@material-ui/core/Button";
export default function EffectHooks() {
const [randNumber, setNumber] = useState(10);
useEffect(() => {
console.log("use effect");
return () => {
console.log("unmounting");
};
});
return (
<Card>
<CardContent>
Random Number: {randNumber}
<p>
<Button
variant="contained"
color="primary"
onClick={() => {
setNumber(Math.floor(Math.random() * 10));
}}
>
Change Name
</Button>
</p>
</CardContent>
</Card>
);
}
Explanation:
- When component render for the first time, it called useEffect and in log you can see it is printing “use effect:10” (similar to componentDidMount)
- We can see that in this effect we are also accessing our state variables (as this is available in our function directly)
- Clicking on a button is calling setNumber to generate a random number and set the value for randNumber.
- Once value is set, it will re-render the component and you will see that useEffect() is called again (similar to componentDidUpdate).
- But this time it logged “unmounting” & then “use effect” This is because everytime component re-renders it will first unmount the previous component and hence calling “unmounting” and then calling the useEffect() method again which is again printing “use effect”
So from above we are able to generate behaviours which are similar (not 100% same) to class lifecycle hooks:
- ComponentDidMount
- ComponentDidUpdate
- ComponentWillUnmount
Interesting point in the above code is that it is not only doing the cleanup process when component is unmounting but every time when useEffect is being called. So actually it lets you run a side effect before and after every render. It is also noted that it is not mandatory to do the cleanup and you can decide based on your requirement if cleanup is required.
Example:In case of api hit you won’t require but in case of subscription you might require it to prevent your component from memory leaks.
We can also see that useEffect() is helping us to add the similar functionality in a single method rather than having to do the same thing in multiple methods as in case of classes.
What if I want to limit useEffect to execute only for specific state change ?
As we know that it can sometimes be a performance concern depending upon the requirement where I don’t want to execute it everytime there is a change in any state variable.
In order to fix this issue there is an extra parameter we can add to our useEffect() method which is an array.
So in array we can pass the state variables where we want that this effect should execute whenever there is a change in those passed state variables.
useEffect(() => {
console.log("use effect");
return () => {
console.log("unmounting");
};
},[name]);
In actual components there can be multiple states in our component and hence as per above snippet we want that it will only execute our useEffect hooks only and only if there is a change to my name state hook.
Similarly if you want to watch for multiple states apart from name like “age”, “quotes”, “any state” just add those state variables to this array passed to useEffect() like:
useEffect(() => {
console.log("use effect");
return () => {
console.log("unmounting");
};
},[name, age, quotes]);
Making an api call using useEffect:
useEffect() is the best place where we should be making our api hits. So below is the code to fetch employee details.
File: EffectHooksAPI.js
import React, { useEffect, useState } from "react";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
export default function EffectHooksAPI() {
const [employees, setEmployees] = useState([]);
const [randNumber, setNumber] = useState(10);
useEffect(() => {
console.log("calling use effect");
fetch("http://dummy.restapiexample.com/api/v1/employees")
.then(function (response) {
if (response.status !== 200) {
console.log(
"Looks like there was a problem. Status Code: " + response.status
);
return;
}
// Examine the text in the response
response.json().then(function (data) {
setEmployees(data.data.splice(0, 10));
});
})
.catch(function (err) {
console.log("Fetch Error :-S", err);
});
});
useEffect(() => {
console.log("use effect" + randNumber);
return () => {
console.log("unmounting");
};
}, [randNumber]);
return (
<Card>
<CardContent>
Employee's :
<ul>
{employees.map((empData, id) => {
return <li key={id}>{empData.employee_name}</li>;
})}
</ul>
Random Number: {randNumber}
</CardContent>
</Card>
);
}
Explanation:
- Here we are making an api hit inside our useEffect() using Fetch
- In order to show the fetched data to the UI, we are calling setEmployees() method to populate the employees state.
- Once it is populated you will see in the UI that there are employees shown on the screen.
- But wait, let’s check your dev tool and check the logs. So you can see that it keeps on calling my effect hook endlessly (which is making network hits to fetch data again)
- Endless Loop: We are seeing endless loop because whenever we update the state by calling setEmployees() via that api it will re-render it i.e. function renders again which will again hit the useEffect() hook which will again hit the fetch request i.e. api request which will again call setEmployees() to update the state which will re-render the function again and the cycle repeats.
How to stop this endless loop ?
Just pass [] blank array as dependency parameter to your useEffect() and it will work now.
useEffect(() => {
console.log("calling use effect");
fetch("http://dummy.restapiexample.com/api/v1/employees")
.then(function (response) {
if (response.status !== 200) {
console.log(
"Looks like there was a problem. Status Code: " + response.status
);
return;
}
// Examine the text in the response
response.json().then(function (data) {
setEmployees(data.data);
});
})
.catch(function (err) {
console.log("Fetch Error :-S", err);
});
},[]);
So now you can see that it is working as expected. It is not actually acting as a componentDidMount().
What if I have to make multiple independent api hits required for their own purpose in the same component(i.e. hooks for separate concerns)?
The good thing is that React allows us to define multiple useEffect() hooks inside the same component (same like for useState()). They will be executed in order they are defined in your component.
import React, { useEffect, useState } from "react";
import Card from "@material-ui/core/Card";
import CardContent from "@material-ui/core/CardContent";
export default function EffectHooksAPI() {
const [employees, setEmployees] = useState([]);
const [randNumber, setNumber] = useState(10);
useEffect(() => {
console.log("calling use effect");
fetch("http://dummy.restapiexample.com/api/v1/employees")
.then(function (response) {
if (response.status !== 200) {
console.log(
"Looks like there was a problem. Status Code: " + response.status
);
return;
}
response.json().then(function (data) {
setEmployees(data.data);
});
})
.catch(function (err) {
console.log("Fetch Error :-S", err);
});
});
useEffect(() => {
console.log("use effect");
return () => {
console.log("unmounting");
};
},[randNumber]);
return (
<Card>
<CardContent>
<ul>
{employees.map((empData, id) => {
return <li key={id}>{empData.employee_name}</li>;
})}
</ul>
Random Number: {randNumber}
</CardContent>
</Card>
);
}
Explanation:
- We created two effect hooks which are doing their independent work
- First effect hook is making the api hit to fetch employees data
- Another effect hook is generating the random number and it will only get hit when there is an update to its state which is randNumber.
I hope that now you have much clarity about useEffect() and can use it in your React Apps.
Here is the CodeSandBox if you wanna play around !
I hope this might be helpful, feel free to reach me out in any case.
Link to codebase: Github
Top comments (0)