UNPKG

react-enhanced-debounce-hook

Version:

A tiny, dependency-free React hook library for debouncing values and state. Includes two ergonomic APIs so you can pick the pattern that fits your use-case:

80 lines (79 loc) 3.11 kB
import { useEffect, useRef, useState } from "react"; /** * useDebounce * API-compatible with `use-debounce`: * const [debouncedValue, controls] = useDebounce(value, delay?, options?) * * - debouncedValue: value that follows `value` after `delay` ms of inactivity (or immediately if leading) * - controls: { flush(), cancel() } */ export function useDebounce(value, delay, options) { if (delay === void 0) { delay = 500; } var _a = useState(value), debouncedValue = _a[0], setDebouncedValue = _a[1]; var timeoutRef = useRef(null); var lastRef = useRef(value); var leadingFiredRef = useRef(false); useEffect(function () { lastRef.current = value; }, [value]); useEffect(function () { // clear existing timer if (timeoutRef.current !== null) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } if (options === null || options === void 0 ? void 0 : options.leading) { if (!leadingFiredRef.current) { // fire immediately on leading edge setDebouncedValue(value); leadingFiredRef.current = true; // set timer to reset leading flag at end of window timeoutRef.current = globalThis.setTimeout(function () { timeoutRef.current = null; leadingFiredRef.current = false; }, delay); return function () { if (timeoutRef.current !== null) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } }; } else { // already fired leading in this window — schedule trailing update timeoutRef.current = globalThis.setTimeout(function () { setDebouncedValue(lastRef.current); timeoutRef.current = null; leadingFiredRef.current = false; }, delay); } } else { // normal trailing debounce behaviour timeoutRef.current = globalThis.setTimeout(function () { setDebouncedValue(value); timeoutRef.current = null; }, delay); } return function () { if (timeoutRef.current !== null) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } }; // intentionally include options?.leading in deps // eslint-disable-next-line react-hooks/exhaustive-deps }, [value, delay, options === null || options === void 0 ? void 0 : options.leading]); var cancel = function () { if (timeoutRef.current !== null) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } }; var flush = function () { cancel(); setDebouncedValue(lastRef.current); leadingFiredRef.current = false; }; return [debouncedValue, { flush: flush, cancel: cancel }]; }