Debounce — Wait for the User to Finish Typing
Delay expensive work (like search) until input settles using a simple debounced hook.
Introduction
Debouncing delays running a function until input has “settled” for a short period. It’s a simple way to avoid firing expensive work (like network requests) on every keystroke.
Why this matters
Running a fetch on every keystroke wastes work and can make the UI feel sluggish. Debouncing waits for a pause before running the action.
The problem
Without debouncing, you trigger side effects on every keypress, flooding your app and network.
Inefficient approach
Calling the effect on each input change spams logs/requests:
import { useState, useEffect } from "react";
function App() {
const [searchTerm, setSearchTerm] = useState("");
useEffect(() => {
// Imagine this is a fetch; it runs on every keystroke
console.log("Searching for:", searchTerm);
}, [searchTerm]);
return (
<div>
<h1>Debounce</h1>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
<p>Searching for: {searchTerm}</p>
</div>
);
}
export default App;
The solution
Create a useDebounce hook that returns a stabilized value after a delay.
import { useState, useEffect } from "react";
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debouncedValue;
}
function App() {
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearchTerm) {
console.log("Searching for:", debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<div>
<h1>Debounce</h1>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
<p>Search term: {searchTerm}</p>
<p>Debounced: {debouncedSearchTerm}</p>
</div>
);
}
export default App;
Step-by-step
-
Inside
useDebounce, createdebouncedValuestate and initialize withvalue. -
In an effect, set a timeout to update
debouncedValueafterdelay. -
Clean up the timeout when
valueordelaychanges. -
Use the debounced value when making requests or expensive computations.
Tips
-
Always clean up the timeout to prevent memory leaks.
-
Consider leading vs trailing behavior depending on UX needs.
-
For requests, cancel in‑flight fetches when the debounced value changes.
Knowledge base
Problem snippet:
useEffect(() => console.log("Searching:", searchTerm), [searchTerm]);
Solution snippet:
const debouncedSearchTerm = useDebounce(searchTerm, 500);
// use debouncedSearchTerm for network calls