useDebounceFn()
Last updated: 24/04/2026
Overview
useDebounceFn returns a stable function that delays invoking your callback until the caller has stopped re-invoking it for delay ms (default 500). Each new call resets the timer (leading-edge is not fired; only the trailing edge). The latest fn is always used via a ref, so you do not need to wrap fn in useCallback for correctness. The returned function has a .cancel() method to abort a pending run; that also runs on unmount via an effect. The debounced function returns void (it does not forward the wrapped function’s return value). Ideal for search boxes, resize handlers, and auto-save drafts.
What it accepts
fn: a function to debouncedelay(optional, ms): default 500
What it returns
- A
debouncedfunction withdebounced.cancel()
Usage
400 ms after the user stops typing, run a search; cancel is available if you need to flush-abort (not shown here).
import { useState } from 'react'
import useDebounceFn from '@dedalik/use-react/useDebounceFn'
function Example() {
const [query, setQuery] = useState('')
const [last, setLast] = useState('')
const runSearch = (q: string) => {
setLast(q.trim() || '(empty)')
}
const debouncedSearch = useDebounceFn(runSearch, 400)
return (
<div>
<label>
Search
<input
value={query}
onChange={(e) => {
const v = e.target.value
setQuery(v)
debouncedSearch(v)
}}
/>
</label>
<p>Last search: {last}</p>
</div>
)
}
export default function Demo() {
return <Example />
}API Reference
useDebounceFn
Signature: useDebounceFn<T extends (...args: any[]) => any>(fn: T, delay?: number): DebouncedFunction<T>
Copy-paste hook
TypeScript
import { useCallback, useEffect, useRef } from 'react'
export interface DebouncedFunction<T extends (...args: any[]) => any> {
(...args: Parameters<T>): void
cancel: () => void
}
export default function useDebounceFn<T extends (...args: any[]) => any>(fn: T, delay = 500): DebouncedFunction<T> {
const fnRef = useRef(fn)
const timeoutRef = useRef<number | null>(null)
useEffect(() => {
fnRef.current = fn
}, [fn])
const cancel = useCallback(() => {
if (timeoutRef.current != null) {
window.clearTimeout(timeoutRef.current)
timeoutRef.current = null
}
}, [])
useEffect(() => cancel, [cancel])
const debounced = useCallback(
(...args: Parameters<T>) => {
cancel()
timeoutRef.current = window.setTimeout(() => {
timeoutRef.current = null
fnRef.current(...args)
}, delay)
},
[cancel, delay],
) as DebouncedFunction<T>
debounced.cancel = cancel
return debounced
}JavaScript
import { useCallback, useEffect, useRef } from 'react'
export default function useDebounceFn(fn, delay = 500) {
const fnRef = useRef(fn)
const timeoutRef = useRef(null)
useEffect(() => {
fnRef.current = fn
}, [fn])
const cancel = useCallback(() => {
if (timeoutRef.current != null) {
window.clearTimeout(timeoutRef.current)
timeoutRef.current = null
}
}, [])
useEffect(() => cancel, [cancel])
const debounced = useCallback(
(...args) => {
cancel()
timeoutRef.current = window.setTimeout(() => {
timeoutRef.current = null
fnRef.current(...args)
}, delay)
},
[cancel, delay],
)
debounced.cancel = cancel
return debounced
}