import { useEffect , useMemo , useRef , useState } from 'react' ; type DebounceSettings = { leading ? : boolean ; } ; export function debounce < A extends Function > ( func , wait ? , leading ? ) { let timeout ; let isCancelled = false ; function debounced ( this ) { isCancelled = false ; let context = this ; let args = arguments ; if ( leading && ! timeout ) { func . apply ( context , args ) ; } clearTimeout ( timeout ) ; timeout = setTimeout ( function ( ) { timeout = null ; if ( ! leading && ! isCancelled ) { func . apply ( context , args ) ; } isCancelled = false ; } , wait ) ; } debounced . cancel = ( ) => { isCancelled = true ; } ; return debounced as any ; } const defaultOpts = { leading : false } ; export function useDebounce < A extends ( ... args : any ) => any | undefined | null , DebouncedFn extends A & { cancel : ( ) => void ; } > ( fn , wait , options = defaultOpts , mountArgs = [ ] ) { const dbEffect = useRef < DebouncedFn | null > ( null ) ; useEffect ( ( ) => { return ( ) => { dbEffect . current ?. cancel ( ) ; } ; } , [ ] ) ; return useMemo ( ( ) => { dbEffect . current = debounce ( fn , wait , options . leading ) as unknown as DebouncedFn ; return dbEffect . current ; } , [ options . leading , ... mountArgs ] ) ; } /**
 * Returns a value once it stops changing after "amt" time.
 * Note: you may need to memo or this will keep re-rendering
 */ export function useDebounceValue < A > ( val , amt = 0 ) { const [ state , setState ] = useState ( val ) ; useEffect ( ( ) => { let tm = setTimeout ( ( ) => { setState ( ( prev ) => { if ( prev === val ) return prev ; return val ; } ) ; } , amt ) ; return ( ) => { clearTimeout ( tm ) ; } ; } , [ val ] ) ; return state ; }