DIY: Writing custom React Hooks to perform async operations
Tags: react, hooks, javascript, reactnative
Published: 2020-10-06 05:19:01


This article is about creating your own React Hooks to perform async operations. As an example, we will create a custom *useFetch* Hook to make API calls.

# What are Hooks? 
[Hooks](https://reactjs.org/docs/hooks-intro.html) are functions which let you use state and other [React](https://reactjs.org/) features without writing a class. They are a new addition in React 16.8. You can check the [overview](https://reactjs.org/docs/hooks-overview.html) of Hooks before moving ahead. One important thing about Hooks is that they can only be used either in a [functional component](https://reactjs.org/docs/components-and-props.html) or inside another Hook.

# Why custom Hooks?
A custom Hook allows you to extract some components logic into a reusable function. It is a reusable Javascript function that can call other Hooks.

# Rules of Hooks
> Hooks are JavaScript functions, but they impose two additional rules:
 * Only call Hooks **at the top level**. Don't call Hooks inside loops, conditions, or nested functions.
 * Only call Hooks **from React function components**. Don't call Hooks from regular JavaScript functions.

# What are we trying to achieve here?
There are no bounds to the functionality that you can achieve using Hooks. But, in this article, we are just creating a specific type of custom Hook to perform async operations (API calls in this example) and tailor it to fit our use-cases. We will also have a function *fetchNow* that can be used to fetch the data with a callback. This should be the basic API for our example Hook.

```ts
const { data, loading, error } = useFetch(
    "https://www.reddit.com/r/popular.json"
  );
```
Alternative API could be the following.
```ts
const { data, loading, error, fetchNow } = useFetch();
```

We will start with creating our Hook and we will name it *useFetch*. It takes *url* and *options* as parameters. We will use [useState](https://reactjs.org/docs/hooks-state.html) and [useEffect](https://reactjs.org/docs/hooks-effect.html) Hooks internally to implement our Hook.

```ts
function useFetch(url: string, options?: any) {
  const [data, setData] = useState();
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState();

  function fetchNow(url: string, options?: any) {
    // we will add the code here
  }

  useEffect(() => {
      fetchNow(url, options);
  }, []);

  return { data, loading, error, fetchNow };
}
```

To prevent the extra re-renders. We will merge our *setState* Hooks.

```ts
function useFetch(url: string, options?: any) {
  const [status, setStatus] = useState<{
    loading: boolean;
    error?: Error;
    data?: any;
  }>({
    loading: false
  });

  function fetchNow(url: string, options?: any) {
    // we will add the code here
  }

  useEffect(() => {
      fetchNow(url, options);
  }, []);

  return { ...status, fetchNow };
}
```

Now, we have the bare-bones of our Hook ready. You can add the code according to the functionality of the Hook you are creating. In our case, we need to add the API calls. We will use the [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API for this. After adding the logic, our function looks like this.

```ts
function useFetch(url?: string, options?: any) {
  const [status, setStatus] = useState<{
    loading: boolean;
    error?: Error;
    data?: any;
  }>({
    loading: false
  });
  function fetchNow(url: string, options?: any) {
    setStatus({ loading: true });
    fetch(url, options)
      .then((res: any) => res.json())
      .then((res: any) => {
        setStatus({ loading: false, data: res.data });
      })
      .catch((error: Error) => {
        setStatus({ loading: false, error });
      });
  }

  useEffect(() => {
    if (url) {
      fetchNow(url, options);
    }
  }, []);

  return { ...status, fetchNow };
}
```
The function is complete now. We will use them in our functional component like the initially expected API or with a callback like in the code shown below. And we will get the fetched data status in the variables named ***data, loading, error***.

```tsx
<button onClick={() => fetchNow("https://www.reddit.com/r/popular.json")}>
  Fetch data
</button>
```

# TL;DR
You can check the sandbox below for the complete functionality of Hook.
{% codesandbox usefetch-39ql4 %}

# What's next?
* **Cancelling Request**: We can add the option to cancel the requests.
* **Caching**: We can add a caching layer so that it does not have to make API calls for the same requests multiple times.

* **Central Error Handling**: We can add an option to dispatch the error to a central handler in the project.

# Conclusion
This is just one of the common use-cases of custom hooks. You can achieve a lot of great things with them. You got the idea on how to create custom Hooks. Here, we just made the API calls inside the Hook, but you can do all sorts of async operations using the same idea.

Thanks for reading and do give it a 💖 if you found it helpful!
Happy coding!