Ratios — a React hook library for managing axios requests, includes cancellation mechanism
Tags: react, axios
Published: 2021-06-28 09:43:05

# This article is written for Ratios 1.x.
## For Ratios 2.x, please check npm or Github for documentation.

Many of us have used [axios](https://github.com/axios/axios) — a Promise based HTTP client for the browser and NodeJS in React projects. There are a lot of React hook libraries that help us bring axios into our apps, but none of them handle cancel token for you.

So what does a cancel token do? Why is it so important? Please take a look at the following component first:

![Example component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jeiy88wbuh5df58qhopu.gif)

This is a component which shows text “Loading…” when an API call is still going on, and display the result after getting the response. Nothing special. You can find the demo on [Stackblitz](https://stackblitz.com/edit/axios-single-page-sample?file=src/App.js).

{% stackblitz axios-single-page-sample %}

This component works great when there is only 1 page in our app. But as you can see, we didn’t get the response immediately — it took a couple seconds before we got the response from server.

So what if we add react-router in our app, and navigate to another component before we get the response?

Well, this happened.

![Navigate to another component before the API responds](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9yayi1hdbg5e2wgi5pwk.gif)

We got an error message from React, saying that we “**Can’t perform a React state update on an unmounted component.**” You can find the demo on [Stackblitz](https://stackblitz.com/edit/axios-with-react-router?file=src/App.js).

{% stackblitz axios-with-react-router %}

![Error we got when we navigate to another component before API responds](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wkcs09b0c2u0qi9f1nbj.png)

Seems familiar, uh? What does this mean? Well, the message is actually very straight to the point, but we still don’t know what is happening. It works great when there’s only 1 page in our app!

If you take a look at the code of User component, you will see something like this:

```javascript
import React, { useState, useEffect } from "react";
import UserAPI from "../apis/User";

export default function App() {
  const [users, setUsers] = useState({
    isLoading: true,
    data: []
  });

  useEffect(() => {
    const fetchUser = async () => {
      const { data } = await UserAPI.getAll();
      const { data: users } = data;
      setUsers({
        isLoading: false,
        data: users
      });
    };

    fetchUser();
  }, []);

  return (
    <div>
      <h1>Users</h1>
      {users.isLoading ? (
        "Loading..."
      ) : (
        <ol>
          {users.data.map(user => (
            <li key={user.id}>
              {user.last_name} {user.first_name}
            </li>
          ))}
        </ol>
      )}
    </div>
  );
}

```

We first declare a state named “users” with { isLoading: true, data: [] }, call an API after the component is mounted, and finally set the response to the “users” state we have declared. Nothing special, just like how everyone else does it. Then what’s the problem?

The problem is that **the API call is still going on after the component is unmounted**. We only told our app to call an API and update states based on the API response, but we didn’t tell it to cancel the request when the component is unmounted/destroyed.

So if we navigate to Home before the API responds, our app will still trying to execute setUsers logic when it receives the response, which leads to the error we saw above — “Can’t perform a React state update on an unmounted component.”. Because the User component is already unmounted, which means the “users” state no longer exists.

Great, now we finally know what is going on here! If we want to get rid of this problem, we just need to cancel the request when the component unmounts (same logic can be applied to any asynchronous behaviour, promise is just one of them).

The usage of CancelToken has been clearly described in the official documentation of axios, you can go check it out [here](https://github.com/axios/axios#cancellation) if you’re curious about how to use it. You can find the working demo with native axios cancellation on [Stackblitz](https://stackblitz.com/edit/axios-with-react-router-and-cancel-token?file=src/App.js).

{% stackblitz axios-with-react-router-and-cancel-token %}

After applying cancellation mechanism to our app, the strange error is forever gone. Wonderful! But is there any React hook library that does all of the stuff described above for me? Good news, yes, that’s why I made **Ratios**.

**Ratios** is a React hook library for managing axios requests, includes cancellation mechanism. It significantly reduce the lines of our codes. Take getUser API we just demonstrated for example:

![Code comparison before and after using ratios](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vrt9mn57x9esi6gdll0r.png)

The code on the left is doing exactly the same thing as the code on the right, and we have reduced the size of code from about 25 lines to only 3 lines.

It merges these steps into one:
1. Declare a state with **useState** hook
2. Create a new **CancelTokenSource** with **useRef** hook
3. Call an API in an **useEffect** hook, and trigger the cancel token we got from step 2 in clean-up function
4. Set API response data to the state we declared in step 1

So the final code will look like this:

```javascript
import React from "react";
import { useAxiosRequest } from "ratios";
import UserAPI from "../apis/User";

const UserComponent = () => {
  const getUsersRequest = useAxiosRequest(UserAPI.getAll, {
    immediate: true, // Execute API immediately
  });

  return (
    <div>
      {getUsersRequest.isLoading ? "Loading..." :
      JSON.stringify(getUsersRequest.data)}
    </div>
  );
};

export default UserComponent;
```

You can find the working demo of Ratios on [Stackblitz](https://stackblitz.com/edit/axios-with-ratios).

{% stackblitz axios-with-ratios %}

As for the library and detailed documentation, you can find it on Github.

{% github abemscac/ratios %}

I hope this article can help you solve your problem, and most importantly, to help you get better. Thank you all.