DEV Community

Cover image for React Custom Hook: useAsync
Sergey Leschev
Sergey Leschev

Posted on

React Custom Hook: useAsync

In this article series, we embark on a journey through the realm of custom React hooks, discovering their immense potential for elevating your development projects. Our focus today is on the "useAsync" hook, one of the many carefully crafted hooks available in the collection of React custom hooks.

Github: https://github.com/sergeyleschev/react-custom-hooks

import { useCallback, useEffect, useState } from "react"

export default function useAsync(callback, dependencies = []) {
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState()
    const [value, setValue] = useState()

    const callbackMemoized = useCallback(() => {
        setLoading(true)
        setError(undefined)
        setValue(undefined)
        callback()
            .then(setValue)
            .catch(setError)
            .finally(() => setLoading(false))
    }, dependencies)

    useEffect(() => {
        callbackMemoized()
    }, [callbackMemoized])

    return { loading, error, value }
}
Enter fullscreen mode Exit fullscreen mode

The useAsync hook takes in a callback function that performs the asynchronous operation and an optional array of dependencies. It returns an object with three properties: loading, error, and value. The loading property indicates whether the operation is currently in progress, while the error property holds any error messages encountered during the process. Finally, the value property contains the resolved value of the asynchronous operation.

One of the significant advantages of useAsync is its ability to memoize the callback function using useCallback. This ensures that the callback is only recreated when the dependencies change, preventing unnecessary re-renders and optimizing performance. Additionally, the hook employs the useState and useEffect hooks to manage the loading state and invoke the memoized callback function when necessary.

UseAsync can be employed in a wide range of scenarios. Whether you're fetching data from an API, performing computations, or handling form submissions, this custom hook simplifies the management of asynchronous operations throughout your React components. Its flexibility and ease of use make it a valuable addition to any React project.

import useAsync from "./useAsync"

export default function AsyncComponent() {
    const { loading, error, value } = useAsync(() => {
        return new Promise((resolve, reject) => {
            const success = false
            setTimeout(() => {
                success ? resolve("Hi") : reject("Error")
            }, 1000)
        })
    })

    return (
        <div>
            <div>Loading: {loading.toString()}</div>
            <div>{error}</div>
            <div>{value}</div>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

By utilizing useAsync, you can streamline your codebase, enhance reusability, and maintain a consistent and reliable user experience. Give it a try in your next React project and witness the power of simplified asynchronous operations.

Full Version | React Custom Hooks:
https://dev.to/sergeyleschev/supercharge-your-react-projects-with-custom-hooks-pl4

Top comments (1)

Collapse
 
sergeyleschev profile image
Sergey Leschev • Edited

You're absolutely right about the potential double triggering in development due to React's behavior. It's essential to be aware of these aspects and handle side effects appropriately, like using cleanup functions for managing resources.

Your suggestion to explore @tanstack/react-query is valuable. Libraries like React Query can offer more comprehensive solutions and address various considerations that arise when dealing with asynchronous operations.

Thanks for the thoughtful input and the cheerful recommendation! 🌟