React hooks library for debouncing and throttling functionality with small footprint and comprehensive control features
—
Value debouncing with useDebounce delays value updates until a specified delay has passed without changes. This is perfect for optimizing React component re-renders when dealing with rapidly changing state values like user input, search queries, or form fields.
Creates a debounced version of a value that only updates after the specified delay period.
/**
* Debounces a value, delaying updates until after delay milliseconds have elapsed
* since the last time the value changed.
*
* @param value - The value to debounce
* @param delay - The number of milliseconds to delay
* @param options - Optional configuration object
* @returns Tuple of [debouncedValue, controlFunctions]
*/
function useDebounce<T>(
value: T,
delay: number,
options?: {
maxWait?: number;
leading?: boolean;
trailing?: boolean;
equalityFn?: (left: T, right: T) => boolean;
}
): [T, DebouncedState<(value: T) => void>];Parameters:
value: T - The value to debounce (can be any type)delay: number - Delay in milliseconds before updating the debounced valueoptions - Optional configuration object with:
maxWait?: number - Maximum time the value is allowed to be delayed before it's updatedleading?: boolean - If true, updates the value immediately on first change, then debounces subsequent changestrailing?: boolean - If true (default), updates the value after the delay periodequalityFn?: (left: T, right: T) => boolean - Custom equality function to determine if the value has changed (defaults to ===)Returns:
[T, DebouncedState] - A tuple containing the debounced value and control functions (cancel, flush, isPending)Usage Examples:
import React, { useState } from 'react';
import { useDebounce } from 'use-debounce';
// Basic text input debouncing
function SearchInput() {
const [searchTerm, setSearchTerm] = useState('');
const [debouncedSearchTerm] = useDebounce(searchTerm, 500);
// This effect will only run when debouncedSearchTerm changes
React.useEffect(() => {
if (debouncedSearchTerm) {
// Perform API search
console.log('Searching for:', debouncedSearchTerm);
}
}, [debouncedSearchTerm]);
return (
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
/>
);
}
// Leading option - immediate first update
function InstantSearch() {
const [query, setQuery] = useState('');
const [debouncedQuery] = useDebounce(query, 1000, { leading: true });
// First change updates immediately, subsequent changes are debounced
return (
<div>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<p>Debounced: {debouncedQuery}</p>
</div>
);
}
// With control functions
function ControlledDebounce() {
const [text, setText] = useState('');
const [debouncedText, { cancel, flush, isPending }] = useDebounce(text, 1000);
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<p>Text: {text}</p>
<p>Debounced: {debouncedText}</p>
<button onClick={cancel}>Cancel</button>
<button onClick={flush}>Flush Now</button>
<p>Pending: {isPending() ? 'Yes' : 'No'}</p>
</div>
);
}
// Custom equality function for objects
function ObjectDebounce() {
const [user, setUser] = useState({ name: '', email: '' });
const [debouncedUser] = useDebounce(
user,
500,
{
equalityFn: (prev, next) =>
prev.name === next.name && prev.email === next.email
}
);
return (
<div>
<input
value={user.name}
onChange={(e) => setUser({ ...user, name: e.target.value })}
placeholder="Name"
/>
<input
value={user.email}
onChange={(e) => setUser({ ...user, email: e.target.value })}
placeholder="Email"
/>
<p>Debounced: {JSON.stringify(debouncedUser)}</p>
</div>
);
}
// MaxWait option
function MaxWaitExample() {
const [value, setValue] = useState('');
const [debouncedValue] = useDebounce(value, 500, { maxWait: 2000 });
// Will update after 500ms of no changes, or after 2000ms maximum
return (
<div>
<input value={value} onChange={(e) => setValue(e.target.value)} />
<p>Debounced (max 2s): {debouncedValue}</p>
</div>
);
}The debounced state includes control functions for managing the debounce behavior:
interface ControlFunctions<ReturnT> {
/** Cancel any pending debounced updates */
cancel: () => void;
/** Immediately execute any pending debounced updates */
flush: () => ReturnT | undefined;
/** Check if there are any pending debounced updates */
isPending: () => boolean;
}Control Methods:
cancel() - Cancels any pending debounced value updatesflush() - Immediately applies any pending debounced value updatesisPending() - Returns true if there's a pending debounced update, false otherwisefunction OptimizedForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
message: ''
});
// Debounce the entire form object
const [debouncedFormData] = useDebounce(formData, 300);
// Only validate when debounced data changes
React.useEffect(() => {
validateForm(debouncedFormData);
}, [debouncedFormData]);
return (
<form>
<input
value={formData.username}
onChange={(e) => setFormData(prev => ({
...prev,
username: e.target.value
}))}
/>
{/* More form fields... */}
</form>
);
}function AutoSuggest() {
const [query, setQuery] = useState('');
const [suggestions, setSuggestions] = useState([]);
const [debouncedQuery] = useDebounce(query, 400);
React.useEffect(() => {
if (debouncedQuery.length > 2) {
fetch(`/api/suggestions?q=${debouncedQuery}`)
.then(res => res.json())
.then(setSuggestions);
} else {
setSuggestions([]);
}
}, [debouncedQuery]);
return (
<div>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<ul>
{suggestions.map(suggestion => (
<li key={suggestion.id}>{suggestion.text}</li>
))}
</ul>
</div>
);
}Install with Tessl CLI
npx tessl i tessl/npm-use-debounce