useDebounce
Debounce espera un periodo sin cambios antes de publicar el último valor. Reduce operaciones repetidas durante una ráfaga, como búsquedas mientras se escribe. Se diferencia de throttle, que permite como máximo una ejecución por intervalo aunque los eventos continúen.
Modelo rápido
Sección titulada «Modelo rápido»entrada: a -- ab -- abc --------debounce: abc ----Cada cambio cancela el timeout anterior. Solo el último valor sobrevive cuando transcurre delayMs sin otra actualización.
import { useEffect, useState } from "react"
export function useDebounce<T>(value: T, delayMs = 300): T { const [debounced, setDebounced] = useState(value)
useEffect(() => { const timeout = window.setTimeout(() => setDebounced(value), delayMs) return () => window.clearTimeout(timeout) }, [value, delayMs])
return debounced}const [query, setQuery] = useState("")const debouncedQuery = useDebounce(query, 350)
useEffect(() => { if (debouncedQuery.trim()) buscar(debouncedQuery)}, [debouncedQuery])Esto retrasa el valor, no una función. Para guardar inmediatamente pero limitar una operación, esta forma mantiene el flujo declarativo: el input usa query; el efecto costoso usa debouncedQuery.
Búsqueda con cancelación
Sección titulada «Búsqueda con cancelación»useEffect(() => { if (!debouncedQuery.trim()) return
const controller = new AbortController()
fetch(`/api/search?q=${encodeURIComponent(debouncedQuery)}`, { signal: controller.signal }) .then((response) => response.json()) .then(setResults) .catch((error) => { if (error.name !== "AbortError") setError("No se pudo buscar") })
return () => controller.abort()}, [debouncedQuery])Debounce reduce el número de solicitudes. AbortController resuelve otro problema: evita que una solicitud anterior continúe o que su respuesta obsoleta gane la carrera.
Límites y decisiones
Sección titulada «Límites y decisiones»- Cancela también el
fetchanterior conAbortController; debounce reduce solicitudes, pero no evita por sí solo que una respuesta vieja llegue después de una nueva. - No lo uses para eventos que deben sentirse instantáneos, como click, foco o validación HTML básica.
- En SSR, el primer valor es el mismo en servidor y cliente, por lo que no introduce mismatch de hidratación.
- Comunica el estado de carga y conserva resultados previos cuando reemplazarlos por un vacío genere parpadeo.
- Para postergar un render costoso sin esperar un tiempo fijo, compara este patrón con
useDeferredValue.