41 lines
1.1 KiB
TypeScript
41 lines
1.1 KiB
TypeScript
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||
|
|
||
|
/**
|
||
|
* Easy way to debounce the handling of a rapidly changing value, e.g. a changing slider input
|
||
|
* @param value value that is rapidly changing
|
||
|
* @param onChange change handler that should receive the debounced updates to the value
|
||
|
* @param debouncedMs how long we should wait for changes to be applied
|
||
|
*/
|
||
|
export default function useDebouncedChangeHandler<T>(
|
||
|
value: T,
|
||
|
onChange: (newValue: T) => void,
|
||
|
debouncedMs = 100
|
||
|
): [T, (value: T) => void] {
|
||
|
const [inner, setInner] = useState<T>(() => value)
|
||
|
const timer = useRef<ReturnType<typeof setTimeout>>()
|
||
|
|
||
|
const onChangeInner = useCallback(
|
||
|
(newValue: T) => {
|
||
|
setInner(newValue)
|
||
|
if (timer.current) {
|
||
|
clearTimeout(timer.current)
|
||
|
}
|
||
|
timer.current = setTimeout(() => {
|
||
|
onChange(newValue)
|
||
|
timer.current = undefined
|
||
|
}, debouncedMs)
|
||
|
},
|
||
|
[debouncedMs, onChange]
|
||
|
)
|
||
|
|
||
|
useEffect(() => {
|
||
|
if (timer.current) {
|
||
|
clearTimeout(timer.current)
|
||
|
timer.current = undefined
|
||
|
}
|
||
|
setInner(value)
|
||
|
}, [value])
|
||
|
|
||
|
return [inner, onChangeInner]
|
||
|
}
|