Correctly handling async/await in React components - Part 2
Tags: react, javascript, codequality
Published: 2020-04-10 15:31:29

[Correctly handling async/await in React components - Part 1](https://dev.to/alexandrudanpop/correctly-handling-async-await-in-react-components-4h74)

## Context
In those blog posts I try to illustrate what problems **async/await** do present for React components and how we can fix them. :white_check_mark:

If you have not checked the previous post yet, please do - so you get more context of what issues can appear with async code in React components:
[Correctly handling async/await in React components](https://dev.to/alexandrudanpop/correctly-handling-async-await-in-react-components-4h74)

As stated in the React community, not handling asyncronous code correctly can lead to a bugfest, so let's see how to deal with it properly. :bug::bug::bug:

{% twitter 1246432321579950081 %}

## The good 
In the previous post [here](https://dev.to/alexandrudanpop/correctly-handling-async-await-in-react-components-4h74), we managed to fix the two issues we had - one about a **React warning when unmounting a component before an async call was finished**. And a second one about **handling concurrent async calls** so we always receive only the latest results for our API calls.

## The bad 
The bad part is that our code is now at aprox twice the initial size in lines of code and looks harder to read. Our initial component was pretty simple. You could simply see that it makes a call to an API and displays a string. 

Now it does too many things - setting up hooks to see if it's mounted, creating cancellation tokens...

## The refactored 
I thought we might want to look at how we can refactor this component to make the code more readable.

The nice thing about **React hooks** is that you can **extract them away** from the components and even **reuse** them when you want.

### The isMouted hook
```jsx
const useIsMounted = () => {
  const isMounted = useRef(false);
  
  useEffect(() => {
    isMounted.current = true;
    return () => (isMounted.current = false);
  }, []);

  return isMounted;
};
```

### The data fetching hook
```jsx
const useJokeAsync = (componentIsMounted, more) => {
  const [joke, setJoke] = useState("");
  useEffect(() => {
    const cancelTokenSource = CancelToken.source();

    async function fetchJoke() {
      try {
        const asyncResponse = await axios(
          "https://api.icndb.com/jokes/random",
          {
            cancelToken: cancelTokenSource.token,
          }
        );
        const { value } = asyncResponse.data;

        if (componentIsMounted.current) {
          setJoke(value.joke);
        }
      } catch (err) {
        if (axios.isCancel(err)) {
          return console.info(err);
        }

        console.error(err);
      }
    }

    fetchJoke();

    return () => {
      // here we cancel preveous http request that did not complete yet
      cancelTokenSource.cancel(
        "Cancelling previous http call because a new one was made ;-)"
      );
    };
  }, [componentIsMounted, more]);

  return joke;
};
```

### And now finally our component :star:
```jsx
export default function RandomJoke({ more, loadMore }) {
  const componentIsMounted = useIsMounted();
  const joke = useJokeAsync(componentIsMounted, more);

  return (
    <div>
      <h1>Here's a random joke for you</h1>
      <h2>{`"${joke}"`}</h2>
      <button onClick={loadMore}>More...</button>
    </div>
  );
}
```
## Now this is much better, but can be improved
We had a a slight issue in our implementation - if you read the first post and this one until here, try to think for 1 min before you scroll down.

Well... if you said the <code>componentIsMounted</code> is redundant you are right :sunglasses:. Why? because all hooks cleanup functions are called on component unmount. That means the cancellation is called before any setState can be called. So having this accidental complexity avoided now we have: 
```jsx
import React, { useState, useEffect } from "react";
import axios, { CancelToken } from "axios";

const useJokeAsync = (more) => {
  const [joke, setJoke] = useState("");
  useEffect(() => {
    const cancelTokenSource = CancelToken.source();

    async function fetchJoke() {
      try {
        const asyncResponse = await axios(
          "https://api.icndb.com/jokes/random",
          {
            cancelToken: cancelTokenSource.token,
          }
        );
        const { value } = asyncResponse.data;

        setJoke(value.joke);
      } catch (err) {
        if (axios.isCancel(err)) {
          return console.info(err);
        }

        console.error(err);
      }
    }

    fetchJoke();

    return () => {
      console.log("Calling cleanup");

      // here we cancel preveous http request that did not complete yet
      cancelTokenSource.cancel(
        "Cancelling previous http call because a new one was made ;-)"
      );
    };
  }, [more]);

  return joke;
};

export default function RandomJoke({ more, loadMore }) {
  const joke = useJokeAsync(more);

  return (
    <div>
      <h1>Here's a random joke for you</h1>
      <h2>{`"${joke}"`}</h2>
      <button onClick={loadMore}>More...</button>
    </div>
  );
}
```
## Conclusions
Extracting code into re-usable hooks can make sense a lot of times, both for the readability of the components and for the isolation of certain concerns like data fetching.

You can check out the code on [Github](https://github.com/alexandrudanpop/Correctly-handling-async-await-in-React).

If you like this post follow me on [Twitter](https://twitter.com/alexandrudanpop) where I post more cool stuff about React and other awesome technologies. :fire::fire::fire: