@equinor/fusion-observable
Version:
40 lines • 1.73 kB
JavaScript
import { useCallback, useMemo, useState } from 'react';
import { from, Subject } from 'rxjs';
import { debounce, debounceTime, switchMap, tap } from 'rxjs/operators';
/**
* React hook that debounces calls to a function and exposes the results as
* an observable stream.
*
* Returns a `next` function (the debounced trigger), a `value$` observable
* that emits results, and an `idle` flag indicating whether a debounced
* call is in-flight.
*
* @template TFn - The wrapped function type.
* @template TType - The return type of `TFn` (must be `ObservableInput`).
* @template TArgs - The argument tuple.
* @param fn - The function to debounce.
* @param options - Debounce configuration.
* @returns An object with `value$`, `next`, and `idle`.
*
* @example
* ```tsx
* const { next: search, value$, idle } = useDebounce(
* (query: string) => fetch(`/api/search?q=${query}`).then((r) => r.json()),
* { debounce: 300 },
* );
*
* useObservableState(value$); // subscribe to results
* search('fusion'); // triggers debounced fetch
* ```
*/
export const useDebounce = (fn, options) => {
const [idle, setIdle] = useState(true);
const [queuer] = useState(() => options?.queuer ?? new Subject());
const next = useCallback((...args) => queuer.next(args), [queuer]);
const debounceFn = useMemo(() => typeof options.debounce === 'function'
? debounce(options.debounce)
: debounceTime(options.debounce), [options.debounce]);
const value$ = useMemo(() => queuer.pipe(debounceFn, tap(() => setIdle(false)), switchMap((args) => from(fn(...args))), tap(() => setIdle(true))), [queuer, debounceFn, fn]);
return { idle, next, value$ };
};
//# sourceMappingURL=useDebounce.js.map