@er-raj-aryan/use-smart-debounce
v0.1.2
Published
Modern React hooks for debouncing values, callbacks, and async functions — with TypeScript support, cancellation, and stale-response protection.
Maintainers
Readme
@er-raj-aryan/use-smart-debounce
Modern React hooks for smarter debouncing — built for real-world apps.
Lightweight, TypeScript-ready, and async-safe. Perfect for search inputs, autocomplete, or any case where you need to wait for users to stop typing before doing work.
Why this package?
Most debounce hooks either:
- 💥 Break on async calls
- ⚙️ Don’t cancel stale API requests
- 🧩 Or require too much setup
This package fixes that — giving you three clean hooks that just work:
useDebouncedValue— debounces simple valuesuseDebouncedCallback— debounces functions/event handlers (leading/trailing/maxWait)useDebouncedAsync— debounced async calls with cancellation + stale-response protection
All in <3 KB, zero dependencies.
Install
npm i @er-raj-aryan/use-smart-debounceor
yarn add @er-raj-aryan/use-smart-debouncegot you. here’s a drop-in README section that adds clear, copy-paste usage examples for all three hooks (including a real MUI Autocomplete wired to your HS Code API). paste this below your Install section (or replace your README with the whole thing).
Quick Start
// App.tsx or any React component
import { useDebouncedValue } from "@er-raj-aryan/use-smart-debounce";
import { useEffect, useState } from "react";
export default function Demo() {
const [q, setQ] = useState("");
const dq = useDebouncedValue(q, 500); // wait 500ms after typing stops
useEffect(() => {
if (!dq) return;
// call your API here with dq
console.log("Search for:", dq);
}, [dq]);
return (
<input
value={q}
onChange={(e) => setQ(e.target.value)}
placeholder="Type…"
/>
);
}Exports
import {
useDebouncedValue,
useDebouncedCallback,
useDebouncedAsync,
} from "@er-raj-aryan/use-smart-debounce";useDebouncedValue<T>(value: T, delay?: number): TuseDebouncedCallback(fn, delay?: number, opts?: { leading?: boolean; trailing?: boolean; maxWait?: number })useDebouncedAsync(fn, delay?: number, opts?: { leading?: boolean; trailing?: boolean; maxWait?: number })
1) useDebouncedValue — debounce any value
Use cases: text inputs, sliders, filters — whenever a value should “settle” before you react.
import { useDebouncedValue } from "@er-raj-aryan/use-smart-debounce";
import { useEffect, useState } from "react";
function PriceFilter() {
const [price, setPrice] = useState(0);
const debouncedPrice = useDebouncedValue(price, 300);
useEffect(() => {
// expensive query only after 300ms of inactivity
console.log("Fetch using price:", debouncedPrice);
}, [debouncedPrice]);
return (
<input
type="number"
value={price}
onChange={(e) => setPrice(Number(e.target.value))}
min={0}
/>
);
}2) useDebouncedCallback — debounce any function/handler
Use cases: onChange, onResize, onScroll, or any callback you don’t want to fire too often.
import { useDebouncedCallback } from "@er-raj-aryan/use-smart-debounce";
import { useState } from "react";
function SearchLogger() {
const [q, setQ] = useState("");
const logTyped = useDebouncedCallback(
(value: string) => {
console.log("User typed:", value);
},
400,
{ leading: false, trailing: true } // call after pause
);
return (
<input
value={q}
onChange={(e) => {
setQ(e.target.value);
logTyped(e.target.value);
}}
placeholder="Type…"
/>
);
}Methods:
debounced.cancel()— cancel pending calldebounced.flush(...args)— immediately invoke now
logTyped.cancel();
logTyped.flush("force run now");3) useDebouncedAsync — debounce async calls (cancel + stale-safe)
Use cases: live search, autocomplete, server-side lookups.
- Cancels previous in-flight promise.
- Ignores stale responses (race-condition safe).
import { useDebouncedAsync } from "@er-raj-aryan/use-smart-debounce";
import { useEffect, useState } from "react";
function LiveSearch() {
const [q, setQ] = useState("");
const { run, status, data, error } = useDebouncedAsync(
async (query: string) => {
if (query.length < 3) return [];
const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
return res.json();
},
500, // wait 500ms after user stops typing
{ trailing: true }
);
useEffect(() => {
run(q);
}, [q]);
return (
<div>
<input value={q} onChange={(e) => setQ(e.target.value)} />
{status === "loading" && <p>Loading…</p>}
{error && <p style={{ color: "red" }}>Error</p>}
<pre>{JSON.stringify(data, null, 2)}</pre>
{/* Also available: run.cancelInFlight() */}
</div>
);
}API Reference
useDebouncedValue<T>(value: T, delay = 300): T
Returns a debounced copy of value that updates after delay ms of inactivity.
useDebouncedCallback(fn, delay = 300, opts?)
leading?: boolean— fire immediately on first call in a burst (defaultfalse)trailing?: boolean— fire after the last call in a burst (defaulttrue)maxWait?: number— ensure execution after this time even if calls continue
Also exposes:
cancel()— cancel pending callflush(...args)— invoke immediately
useDebouncedAsync(fn, delay = 300, opts?)
Same options as above. Returns:
run(...args)— debounced async runner (also hascancelInFlight())- Getters:
status("idle" | "loading" | "success" | "error"),data,error
Import Notes
ESM:
import { useDebouncedValue } from "@er-raj-aryan/use-smart-debounce";CommonJS:
const { useDebouncedValue } = require("@er-raj-aryan/use-smart-debounce");
SSR (Next.js)
These hooks are client-side by nature. In Next.js 13+/App Router, put components using them under a "use client" file boundary.
"use client";
import { useDebouncedValue } from "@er-raj-aryan/use-smart-debounce";Troubleshooting
- TypeScript DTS error in your app Ensure you’re on TypeScript ≥ 5.4 and React types ≥ 18.
- No results while typing
Check your condition (
query.length < 3) and the API path. - Old results flashing
Use
useDebouncedAsync(it cancels/ignores stale responses).
License
MIT © 2025 Er Raj Aryan
