# All files / src useDebouncedFunction.ts

0% Statements 0/117
0% Branches 0/1
0% Functions 0/1
0% Lines 0/117

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
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118

     "use client";
    import { useEffect, useMemo, useRef } from "react";
    import { type CancelableFunction, type AnyFunction } from "./types.js";
    import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect.js";

    /**
     * @remarks \@since 6.0.0
     */
    export type DebouncedFunction<F extends AnyFunction> = CancelableFunction<
      (...args: Parameters<F>) => void
    >;

    /**
     * Creates a function that will only be called if it has not been called again
     * for X milliseconds.
     *
     * @example
     * Debounced Search API Requests
     * ```tsx
     * import { TextField, useDebouncedFunction, useUnmounted } from "@react-md/core";
     * import { useState } from "react";
     * import type { ReactElement } from "react";
     *
     * interface State {
     *   error?: unknown
     *   loading: boolean;
     *   results?: {
     *     // pretend some search results
     *     id: string;
     *     name: string;
     *   }[];
     * }
     *
     * function Example(): ReactElement {
     *   const [state, setState] = useState<State>({
     *     loading: false,
     *   });
     *   // this is only required for async actions
     *   const unmounted = useUnmounted();
     *
     *   // A new search request will be fired once every 500ms as the user types.
     *   // can't use the event here since React uses synthetic events
     *   const search = useDebouncedFunction(async (q: string) => {
     *     setState({
     *       loading: true,
     *       error: undefined,
     *       results: undefined,
     *     });
     *
     *     try {
     *       const response = await fetch('/search', {
     *         method: 'POST',
     *         headers: {
     *           'Content-Type': 'application/json',
     *         },
     *         body: JSON.stringify({ q }),
     *       });
     *       const json = await response.json();
     *
     *       if (!unmounted.current) {
     *         setState({
     *           loading: false,
     *           results: json,
     *         });
     *       }
     *     } catch (error) {
     *       if (!unmounted.current) {
     *         setState({
     *           error,
     *           loading: false,
     *         });
     *       }
     *     }
     *   }, 500);
     *
     *   return (
     *     <TextField
     *       type="search"
     *       label="Search"
     *       onChange={(event) => search(event.currentTarget.value)}
     *     />
     *   );
     * }
     * ```
     *
     * @see `useThrottledFunction` for throttle behavior instead. (Call a
     * function at most once every X milliseconds).
     * @remarks \@since 6.0.0
     */
    export function useDebouncedFunction<F extends AnyFunction>(
      func: F,
      wait: number
    ): DebouncedFunction<F> {
      const timeout = useRef<number | undefined>();
      const funcRef = useRef(func);
      useIsomorphicLayoutEffect(() => {
        funcRef.current = func;
      });

      useEffect(() => {
        return () => {
          window.clearTimeout(timeout.current);
        };
      }, []);

      return useMemo(() => {
        const debounced: DebouncedFunction<F> = (...args) => {
          window.clearTimeout(timeout.current);
          timeout.current = window.setTimeout(() => {
            funcRef.current(...args);
          }, wait);
        };
        debounced.cancel = () => window.clearTimeout(timeout.current);

        return debounced;
      }, [wait]);
    }