Creating a custom useAsync hook for handling asynchronous operations

In this blog post, we will explore how to create a custom useAsync hook in React that simplifies handling asynchronous operations.

Table of Contents

Introduction

Asynchronous operations, such as making HTTP requests, fetching data from an API, or performing time-consuming computations, are common in web development. React provides an excellent way to handle these operations using hooks. However, managing the asynchronous behavior can become complex and repetitive.

The Problem

When dealing with asynchronous operations in React components, we often need to handle multiple states - loading, success, error, etc. We also need to handle edge cases, such as canceling an ongoing operation when the component unmounts.

Solution: Creating the useAsync Hook

To simplify handling asynchronous operations, we can create a custom useAsync hook. This hook encapsulates all the necessary logic and provides a clean and reusable way to handle various asynchronous scenarios.

The Hook Implementation

import { useState, useEffect } from 'react';

const useAsync = (asyncFn, immediate = true) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (immediate) {
      execute();
    }
  }, []);

  const execute = async () => {
    try {
      setLoading(true);
      const result = await asyncFn();
      setData(result);
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  };

  return { data, loading, error, execute };
};

export default useAsync;

The useAsync hook accepts two parameters:

The hook returns an object with the following properties:

Usage Example

Let’s see how we can use the useAsync hook in a React component:

import React from 'react';
import useAsync from './useAsync';

const fetchData = async () => {
  // Simulating an asynchronous API call
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('Data fetched successfully');
    }, 2000);
  });
};

const App = () => {
  const { data, loading, error, execute } = useAsync(fetchData);

  return (
    <div>
      {error && <div>Error: {error.message}</div>}
      {loading ? (
        <div>Loading...</div>
      ) : (
        <div>{data}</div>
      )}
      <button onClick={execute}>Fetch Data</button>
    </div>
  );
};

export default App;

In this example, we use the useAsync hook to handle an asynchronous operation of fetching data. The hook manages the loading state, error handling, and executes the fetchData function. We can manually trigger the execution of the function by clicking the Fetch Data button.

Conclusion

Creating a custom useAsync hook offers a clean and reusable way to handle asynchronous operations in React. By encapsulating the logic within the hook, we can simplify the code within our components and handle common scenarios efficiently.