From aaea04b8a358e5df252155cd0f84594aa18bcabc Mon Sep 17 00:00:00 2001 From: Brian Ingles Date: Thu, 25 Jan 2024 18:13:45 -0600 Subject: [PATCH] Added isDebouncing flag to useDebouncedValue #1747 --- packages/react-hooks/src/useDebouncedValue.ts | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/react-hooks/src/useDebouncedValue.ts b/packages/react-hooks/src/useDebouncedValue.ts index c9efad5319..93c9969ea8 100644 --- a/packages/react-hooks/src/useDebouncedValue.ts +++ b/packages/react-hooks/src/useDebouncedValue.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; /** * Debounces a value. @@ -6,20 +6,39 @@ import { useEffect, useState } from 'react'; * Returns the latest value after no changes have occurred for the debounce duration. * @param value Value to debounce * @param debounceMs Amount of time to debounce - * @returns The debounced value + * @returns The debounced value + whether the value is still debouncing */ -export function useDebouncedValue(value: T, debounceMs: number): T { +export function useDebouncedValue( + value: T, + debounceMs: number +): { isDebouncing: boolean; value: T } { + const [isDebouncing, setIsDebouncing] = useState(true); const [debouncedValue, setDebouncedValue] = useState(value); + + // Set isDebouncing to true immediately whenever the value changes. Using + // `useMemo` instead of `useEffect` so that state is never out of sync whenever + // value and / or debounceMs have changed. + useMemo(() => { + setIsDebouncing(true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [value, debounceMs]); + useEffect(() => { + let isCancelled = false; + const timeoutId = setTimeout(() => { - setDebouncedValue(value); + if (!isCancelled) { + setIsDebouncing(false); + setDebouncedValue(value); + } }, debounceMs); return () => { + isCancelled = true; clearTimeout(timeoutId); }; }, [value, debounceMs]); - return debouncedValue; + return { isDebouncing, value: debouncedValue }; } export default useDebouncedValue;