I found this code from this answer /a/68780417/26420821
How does it work?
export function useSearchDebounce(delay = 350) {
const [search, setSearch] = useState(null);
const [searchQuery, setSearchQuery] = useState(null);
useEffect(() => {
const delayFn = setTimeout(() => {
console.log("setTimeout called");
setSearch(searchQuery);
}, delay);
return () => clearTimeout(delayFn);
}, [searchQuery, delay]);
return [search, setSearchQuery];
}
Usage :
const [search, setSearch] = useSearchDebounce();
<input onChange={(e) => setSearch(e.target.value)}/>
If we assume user types “abc” together in the input field with delay set to 5000,
At first, searchQuery
will be "a"
, it will setTimeout()
to run after 5 secs,
Then searchQuery
will be "ab"
, it will again setTimeout()
to run after 5 secs,
Then searchQuery
will be "abc"
, it will again setTimeout(
) to run after 5 secs
But when I tested console.log()
executed just once, Why didn’t setTimeout()
execute 3 times ?
Am I misunderstanding something ?
I don’t understand the code
Baxu is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1
But when I tested
console.log()
executed just once, Why didn’t
setTimeout()
execute 3 times ?
Because every time one of your dependencies change (ie: searchQuery
or delay
), the cleanup function is first run clearing the previous timeout that had been scheduled, then it runs your useEffect callback to schedule a new timeout.
So the behaviour you’re missing is that the previous timeout is cleared when the user types "abc"
:
searchQuery
will be"a"
, clear previous timeout (if one exists, one technically gets scheduled onmount), and schedule a newsetTimeout()
to run after 5 secssearchQuery
will be"ab"
, clear previous timeout (so that never executes its callback now), and schedule a newsetTimeout()
to run after 5 secssearchQuery
will be"abc"
, clear previous timeout (so that never executes its callback now), and schedule a newsetTimeout()
to run after 5 secs
Timeout completes as nothing clears it and you see the setTimeout called
log.
export function Filter() {
const [filter, setFilter] = useState("");
const debounced = useDebounce(filter);
return (
<>
<input value={filter} onChange={(e) => setFilter(e.target.value)} />
<div>{`Debounced Value: ${debounced}`}</div>
</>
);
}
function useDebounce<T>(value: T, delay = 300) {
const [debounced, setDebounced] = useState<T>(value);
useEffect(() => {
const timeoutId = setTimeout(() => {
setDebounced(value);
}, delay);
return () => {
clearTimeout(timeoutId);
};
}, [delay, value]);
return debounced;
}
By separating the input element state and the debounced value you get access to both:
- the immediate updating of the input element
- the delayed update of the debounced value (div element in example)