`useEffect()` and `async`
Tags: javascript, react, codenewbie, webdev
Published: 2020-08-10 14:03:53

If you've learned the traditional class-based React Components and you're now trying to move into Hooks there's a few things along the way that'll throw you for a loop.

One such thing that took a little digging for me is the combination of `useEffect()` - essentially the replacement for `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount` - and `async`/`await`.

## The Situation

Common scenario: Our app loads, we get something on the screen _AND THEN_ we want to fetch some sort of data. We don't want to block the User from moving on or make them stare at a blank screen because our API call is taking longer than expected. 

## `componentDidMount`

This is the method in which we remedied this problem if using React Class-based Components. It ensures that the actual Component is inserted into the DOM tree first and then `render()` is called.

If we _don't need_ an API request to be made then the Component will simply render to the screen. If we do need to make an API request we should do that inside of `componentDidMount()`. In addition if upon receiving the API response we must update, or `setState()`, then that will actually trigger a second render.

Then the calls will look like this:

- componentDidMount()
- render()
- setState() [from inside componentDidMount()]
- render()

Even though there is a second render, the React Docs state that the user **will not see the intermediate state**. So no weird flickering UI - that's good for us! However, the Docs also say to be cautious as this way could lead to performance issues. 

More specifically, any DOM node that needs insertion into the tree belongs in this spot, `componentDidMount()`. If you can React recommends that initialization of `state` be done in the `constructor()` instead of here. Obviously, that isn't always viable but it is what they recommend.

```jsx
// src/api/index.js 
export default {
  async index() {
    const res = await fetch('https://my-json-server.typicode.com/Claim-Academy-JS/products/products')

    return await res.json()
  }
}

/* ------------------ */
import api from 'api'

async componentDidMount() {
  const products = await api.index()
  this.setState({ filteredProducts: products, products })
}
```

This code shows the fetch call being made inside `componentDidMount()` and in this case for my project I needed this setup. These products were to be inserted into the DOM tree so I make the fetch call and set my state. Of course `async` is on both `componentDidMount()` and my exported object's `index()` method. Then inside both of those we `await` the results.

## `useEffect()`

Now with Hooks, more specifically `useEffect()` there's a couple things we need to be aware of. First we must understand that `useEffect()` takes **two arguments**.

**First Argument**
- Callback function

**Second Argument**
- Property to watch for a change -> then fire the provided callback

So as per usual, a callback is needed - no surprise there. The second parameter can cause some problems _if we forget to specify it_. If there is **no second argument provided** the `useEffect()` will fire on every update no matter what is being updated. Even further, if there's a `setState()` or a `useState()` setter being used inside - `useEffect()` will go into an infinite loop.

Let's take a look at an equivalent code to the previous example just instead utilizing `useEffect()`:

```jsx
  useEffect(() => {
    (async () => {
      const products = await api.index()
      setFilteredProducts(products)
      setProducts(products)
    })()
  }, [])

```

This time you see there is an Immediately Invoked Function Expression, or IIFE, inside. We could just as well name that function and then specifically invoke it inside too.

```jsx
  useEffect(() => {
    const fetchProducts = async () => {
      const products = await api.index()
      setFilteredProducts(products)
      setProducts(products)
    }
    fetchProducts()
  }, [])

```

Also take note, we are actually providing a callback to `useEffect()` and _within that callback_ we must define another function and invoke it. That's due to a fetch call returning a Promise. So essentially `useEffect()` itself isn't responsible for that so our defined function will handle it. 

Lastly that **second argument** is present to ensure this `useEffect()` only runs at a specific time.

There we provided `[]` as the second argument. This is telling `useEffect()` "Hey, I only want you to run your callback when this Component mounts for the first time and that's it." By using `[]` we tell `useEffect()` that there are no properties we want you to watch and then run your callback when they change. Just run once.

There's a difference too between `[]` as a second argument and _no second argument_. As mentioned before without a second argument that `useEffect()` will think it should run when the component mounts and then on every single update after regardless of what piece of state changes.

If we wanted a function to run each time a specific piece of state changes you would simply put it inside the brackets like so `[somePropertyNameHere]`.

## Article Will Unmount

It took a little digging to find the way to combine `useEffect()` and `async` functions. It was definitely surprising the the React Docs has no examples of this situation at all that I could find. I referenced a few different 3rd party articles explaining this to be sure I was on the right path by what all of them said. [Robin Wieruch wrote about this](https://www.robinwieruch.de/react-hooks-fetch-data); it was the first one and one of the better articles I found on this topic.

Definitely happy to figure it out because even though this was a weird one to figure out - I still like Hooks more!