# All files / src/hooks useApi.ts

100% Statements 19/19
88.88% Branches 8/9
100% Functions 3/3
100% Lines 19/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50

    12x
    12x
    12x
    12x

    12x
    7x
    7x
    7x
    7x
    7x

    5x
    4x

    2x
    2x

    7x
    6x

    12x
    5x

    12x

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

    interface UseApiState<T> {
      data: T | null;
      loading: boolean;
      error: string | null;
      refetch: () => void;
    }

    /**
     * Generic hook for API calls with loading/error states.
     * Uses a request counter so only the latest fetch wins (prevents race conditions).
     */
    export function useApi<T>(
      fetcher: () => Promise<T>,
      deps: unknown[] = [],
    ): UseApiState<T> {
      const [data, setData] = useState<T | null>(null);
      const [loading, setLoading] = useState(true);
      const [error, setError] = useState<string | null>(null);
      const requestId = useRef(0);

      const fetch = useCallback(async () => {
        const thisRequest = ++requestId.current;
        setLoading(true);
        setError(null);
        try {
          const result = await fetcher();
          // Only apply result if this is still the latest request
          if (thisRequest === requestId.current) {
            setData(result);
          }
        } catch (e) {
          Eif (thisRequest === requestId.current) {
            setError(e instanceof Error ? e.message : 'Unknown error');
          }
        } finally {
          if (thisRequest === requestId.current) {
            setLoading(false);
          }
        }
      }, deps);

      useEffect(() => {
        fetch();
      }, [fetch]);

      return { data, loading, error, refetch: fetch };
    }