13
React Hooks Explained: useEffect( ) (By Building An API Driven App)
Original Interactive Post Link => https://webbrainsmedia.com/blogs/react-hooks-explained-useEffect-hook
In the previous article, I talked about the useState
react hook. In this article, We will talk about the useEffect
hook. which gives us the combined ability of these three famous React class lifecycle methods => componentDidMount
, componentDidUpdate
and componentWillUnmount
. So, Lets start exploring this powerful hook by building a Coronavirus Tracker Application.
Let's start by first defining the basic React functional component.
import React from 'react';
export const CoronaApp = () => {
const renderButtons = () => {
return (
<div>
<button style={{ margin: '5px' }}>Worldwide</button>
<button style={{ margin: '5px' }}>USA</button>
<button style={{ margin: '5px' }}>India</button>
<button style={{ margin: '5px' }}>China</button>
</div>
);
};
return (
<div>
<h2>Corona Tracker</h2>
{renderButtons()}
</div>
);
};
Now, let's define two states =>
- data: To store the tracking data that is fetched from the API
- region: To store the current region
import React, { useState } from 'react';
export const CoronaApp = () => {
const [data, setData] = useState({});
const [region, setRegion] = useState('all');
const renderButtons = () => {
return (
<div>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('all');
}}
>
Worldwide
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('usa');
}}
>
USA
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('india');
}}
>
India
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('china');
}}
>
China
</button>
</div>
);
};
return (
<div>
<h2>Corona Tracker</h2>
{renderButtons()}
<h4 style={{ marginTop: '10px' }}>{region.toUpperCase()}</h4>
</div>
);
};
Now, We will use axios
to fetch the data from the API inside our useEffect
hook. But before that, Let's first look at the basic usage of useEffect
.
The most basic way to use the useEffect
hook is by passing a single function as its argument like this =>
useEffect(() => {
console.log('I will run on every render');
});
By defining useEffect
like this, Makes this hook behave like componentDidUpdate
lifecycle method meaning it will run on every single render of our functional component.
To make the useEffect
to behave like componentDidMount
i.e. Make it to run only on the first render of our functional component. We need to pass an empty array as the second argument in the useEffect
hook like this =>
useEffect(() => {
console.log('I will run only on first render');
}, []);
We can also pass a value in the array. By doing this, We are depending the running of useEffect
hook on the state of the value passed. Like if we take an example of our corona tracker app, We want our useEffect
to only run when the value of the region
changes. So, We will define our useEffect
hook like this =>
useEffect(() => {
// Data fetching from the API
}, [region]);
Okay! So now let's get back to our tracker app and use the useEffect
hook to fetch the data from the API.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export const CoronaApp = () => {
const [data, setData] = useState({});
const [region, setRegion] = useState('all');
useEffect(() => {
axios
.get(
region === 'all'
? `https://corona.lmao.ninja/v2/${region}`
: `https://corona.lmao.ninja/v2/countries/${region}`
)
.then((res) => {
setData(res.data);
});
}, [region]);
const renderButtons = () => {
return (
<div>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('all');
}}
>
Worldwide
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('usa');
}}
>
USA
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('india');
}}
>
India
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('china');
}}
>
China
</button>
</div>
);
};
return (
<div>
<h2>Corona Tracker</h2>
{renderButtons()}
<h4 style={{ marginTop: '10px' }}>{region.toUpperCase()}</h4>
<ul>
{Object.keys(data).map((key, i) => {
return (
<li key={i}>
{key} : {typeof data[key] !== 'object' ? data[key] : ''}
</li>
);
})}
</ul>
</div>
);
};
Lets Quickly also add a collapse info button.
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export const CoronaApp = () => {
const [data, setData] = useState({});
const [region, setRegion] = useState('all');
const [inDetail, setInDetail] = useState(false);
const dataLen = Object.keys(data).length;
useEffect(() => {
axios
.get(
region === 'all'
? `https://corona.lmao.ninja/v2/${region}`
: `https://corona.lmao.ninja/v2/countries/${region}`
)
.then((res) => {
setData(res.data);
});
}, [region]);
const renderButtons = () => {
return (
<div>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('all');
}}
>
Worldwide
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('usa');
}}
>
USA
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('india');
}}
>
India
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setRegion('china');
}}
>
China
</button>
<button
style={{ margin: '5px' }}
onClick={() => {
setInDetail(!inDetail);
}}
>
{inDetail ? 'Show Less' : 'Show More'}
</button>
</div>
);
};
return (
<div>
<h2>Corona Tracker</h2>
{renderButtons()}
<h4 style={{ marginTop: '10px' }}>{region.toUpperCase()}</h4>
<ul>
{Object.keys(data).map((key, i) => {
return (
<span key={i}>
{i < (!inDetail ? 10 : dataLen) ? (
<li key={i}>
{key} : {typeof data[key] !== 'object' ? data[key] : ''}
</li>
) : (
''
)}
</span>
);
})}
</ul>
</div>
);
};
Now, If you open the developer console
and go to the network
tab, you will notice that when you click on the 'Show Less/Show More' button, the useEffect
will not run. It will only run when you change the value of the region
by clicking on any country button. That is happening because we passed the value of region
in the array as the second argument of our useEffect
hook. If we remove the region
from the array it will run only the first time and if we remove the array then, it will run everytime on every state change event.
If you have used React then, you must be familiar with this warning that comes up in the console
Can't perform a React state update on an unmounted component. This is a no-op,
but it indicates a memory leak in your application. To fix, cancel all
subscriptions and asynchronous tasks in a useEffect cleanup function.
This message is simply saying to us that please don't try to change the state of a component which has already been unmounted and its unavailable.
This error is very common when we subscribe to a service but forgot to unsubscribe or a component gets unmounted before finishing our async operation. To prevent this we can run a cleanup inside our useEffect
hook.
To do a cleanup, Simply return a function within the method in the useEffect
hook like this =>
useEffect(() => {
console.log('Doing some task like subscribing to a service');
return () => {
console.log('Cleaning up like unsubscribing to a service');
};
});
If you observe the console you will see the running pattern like this =>
You can see that the cleanup will run before the task in useEffect
skipping the first run of the useEffect
hook. The cleanup will also run when the component will get unmounted.
Thats it, that is all you need to know about the useEffect
hook. If you like my articles please consider liking, commenting and sharing them.
Cheers 🍻!!
Original Interactive Post Link => https://webbrainsmedia.com/blogs/react-hooks-explained-useEffect-hook
13