React Hooks Explained: useEffect( ) (By Building An API Driven App)
Tags: react, javascript, hooks
Published: 2021-06-27 20:46:29

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.

## The Coronavirus Tracker App

Let's start by first defining the basic React functional component.

```jsx
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 =>

<ul>
  <li>data: To store the tracking data that is fetched from the API</li>
  <li>region: To store the current region</li>
</ul>

```jsx
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 =>

```jsx
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 =>

```jsx
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 =>

```jsx
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.

```jsx
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.

```jsx
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.

## `useEffect` Clean Up

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 =>

```jsx
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 =>

## Output:

![Clean Up](https://webbrainsmedia.com/static/180d1d4635c2a5cdc109c1362a70975a/1d180/cleanup.png 'Clean Up')

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