Skip to main content

All hooks

useAsync

Custom hook that manages the state of an async function.


Pass an async function and optional dependency array. The hook executes it immediately and provides:

  • value — the resolved result (or undefined while loading)
  • error — the caught error (or undefined on success)
  • loading — whether the async function is currently executing
  • retry() — re-execute the async function on demand

Useful for data fetching, API calls, or any async operation where you need reactive state.

Usage

import { useAsync } from '@ts-hooks-kit/core'

function Example() {
  const result = useAsync()
  return <pre>{JSON.stringify(result, null, 2)}</pre>
}

API

function useAsync(asyncFn: () => Promise<T>, deps?: unknown[]): UseAsyncState<T>

Custom hook that manages the state of an async function.

Parameters

NameTypeDefault valueDescription
asyncFn() => Promise<T>-The async function to execute.
deps?unknown[][]Dependencies that trigger re-execution when changed.

Returns

The state of the async operation.

Type declaration

UseAsyncState

Represents the state of an async operation.

NameTypeDescription
error`Errorundefined`
loadingbooleanWhether the async operation is in progress.
retry() => voidFunction to retry the async operation.
value`Tundefined`

Hook

import { useCallback, useEffect, useRef, useState } from 'react'

/**
 * Represents the state of an async operation.
 * @template T - The type of the resolved value.
 */
export type UseAsyncState<T> = {
  /** Whether the async operation is in progress. */
  loading: boolean
  /** The resolved value if the operation succeeded. */
  value: T | undefined
  /** The error if the operation failed. */
  error: Error | undefined
  /** Function to retry the async operation. */
  retry: () => void
}

/**
 * Custom hook that manages the state of an async function.
 * @template T - The type of the resolved value.
 * @param {() => Promise<T>} asyncFn - The async function to execute.
 * @param {unknown[]} [deps] - Dependencies that trigger re-execution when changed.
 * @returns {UseAsyncState<T>} The state of the async operation.
 * @public
 * @see [Documentation](https://usehooks-ts.com/react-hook/use-async)
 * @example
 * ```tsx
 * const { value, error, loading, retry } = useAsync(async () => {
 *   const response = await fetch('/api/data');
 *   return response.json();
 * }, []);
 * ```
 */
export function useAsync<T>(
  asyncFn: () => Promise<T>,
  deps: unknown[] = [],
): UseAsyncState<T> {
  const [state, setState] = useState<{
    loading: boolean
    value: T | undefined
    error: Error | undefined
  }>({
    loading: true,
    value: undefined,
    error: undefined,
  })

  const asyncFnRef = useRef(asyncFn)
  const isMountedRef = useRef(true)

  // Keep asyncFnRef up to date
  useEffect(() => {
    asyncFnRef.current = asyncFn
  }, [asyncFn])

  const execute = useCallback(async () => {
    setState(prev => ({ ...prev, loading: true, error: undefined }))

    try {
      const result = await asyncFnRef.current()
      if (isMountedRef.current) {
        setState({ loading: false, value: result, error: undefined })
      }
    } catch (err) {
      if (isMountedRef.current) {
        setState({
          loading: false,
          value: undefined,
          error: err instanceof Error ? err : new Error(String(err)),
        })
      }
    }
  }, deps)

  const retry = useCallback(() => {
    execute()
  }, [execute])

  useEffect(() => {
    isMountedRef.current = true
    execute()

    return () => {
      isMountedRef.current = false
    }
  }, [execute])

  return {
    loading: state.loading,
    value: state.value,
    error: state.error,
    retry,
  }
}