1- import { useEffect , useMemo , useState } from 'react' ;
1+ import { useEffect , useState } from 'react' ;
22
33/**
44 * Debounces a value.
@@ -13,18 +13,22 @@ export function useDebouncedValue<T>(
1313 debounceMs : number
1414) : { isDebouncing : boolean ; value : T } {
1515 const [ isDebouncing , setIsDebouncing ] = useState ( true ) ;
16- const [ debouncedValue , setDebouncedValue ] = useState < T > ( value ) ;
16+ const [ debouncedValue , setDebouncedValue ] = useState ( value ) ;
1717
18- // Set isDebouncing to true immediately whenever the value changes. Using
19- // `useMemo` instead of `useEffect` so that state is never out of sync whenever
20- // value and / or debounceMs have changed.
21- useMemo ( ( ) => {
18+ // Keep `isDebouncing` in sync with `value` and `debounceMs` by setting state
19+ // during render instead of in `useEffect`
20+ // https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes
21+ const [ previousValue , setPreviousValue ] = useState ( value ) ;
22+ const [ previousDebounceMs , setPreviousDebounceMs ] = useState ( debounceMs ) ;
23+ if ( value !== previousValue || debounceMs !== previousDebounceMs ) {
2224 setIsDebouncing ( true ) ;
23- // eslint-disable-next-line react-hooks/exhaustive-deps
24- } , [ value , debounceMs ] ) ;
25+ setPreviousValue ( value ) ;
26+ setPreviousDebounceMs ( debounceMs ) ;
27+ }
2528
2629 useEffect ( ( ) => {
2730 let isCancelled = false ;
31+ setIsDebouncing ( true ) ;
2832
2933 const timeoutId = setTimeout ( ( ) => {
3034 if ( ! isCancelled ) {
0 commit comments