I was trying to implement the debounce functionality in React inside useEffect.
Although for the below code, if I call debounceFunc
its executing only once whereas if I directly call the debounceFunc3
its not working.
const handleClick = () => {
setEvent((pre) => [...pre, 1]);
};
const delayFunc = () => {
let timer;
return function () {
clearTimeout(timer);
timer = setTimeout(() => console.log("Hello timer 1"), 2000);
};
};
const delayFunc3 = useCallback(() => {
let timer3;
return function () {
clearTimeout(timer3);
timer3 = setTimeout(() => console.log("Hello timeout3"), 2000);
};
}, []);
const debounce = useCallback(delayFunc(), []);
useEffect(() => {
if (event?.length > 0) {
debounce();
delayFunc3()();
}
}, [event]);
In console Hello timer 1
is printed only once whereas Hello timeout3
is printed or each event triggered/changed.
Codebox link to play around:
https://codesandbox.io/s/inspiring-lamport-sbbup?file=/src/App.js:0-872
Couldn't understand what's wring with the way I am calling debounceFunc3
The problem with your code is that you're executing delayFunc3()
multiple times:
delayFunc3()();
The issue with this is that every time you call delayFunc3()()
you're creating a new local timer3
variable within your delayFunc3
function, and then executing the returned function that now references that new local timer3
variable.
This means that if you call delayFunc3()();
, first a new timer3
variable is created, and then the returned function is executed, queueing a new timeout.
If you then call delayFunc3()();
, again, a new timer3
variable is created (initialized to undefined
), and then your returned function is executed. When your returned function is executed you're runnning clearTimeout(timer3);
, which won't be clearing any timer as timer3
here is referring to the new timer3
variable that was initialized and is undefined
, and not the previous timer3
that was created in the previous function delayFunc3()()
call.
As a result, calling delayFunc3()();
again while your first timeout is still running won't have any effect on the first timeout as the timer3
created by this invocation has no relation to the previous timer3
created, and so it won't be cleared.
Unlike this, performing the following:
const debounce = useCallback(delayFunc(), []);
executes your delayFunc()
and creates a new local variable timer
within the scope of delayFunc()
. The returned function from delayFunc()
closes over the timer
variable declared within the delayFunc()
scope. This means that whenever debounce()
is executed, you won't be creating a new timer
variable like you were in your first example, but instead, you will be reusing the timer
created from the initial delayFunc()
call. Each call to debounce()
will reuse the same timer
variable, and so, if debounce()
is called again, the clearTimeout(timer);
will clear the previously queued timer.
Also note that in both of your examples, you're using useCallback()
on two different things. With debounce
, you're calling useCallback()
on the returned function from delayFunc()
. This means that all future rerenders of your component will use the one function reference that was returned from the initial delayFunc()
call when your component mounted. So debounce
refers to the one unique function for all rerenders.
Unlike this, delayFunc3
is using useCallback()
not on the returned function, but rather the entire function (ie: the "parent" arrow function). This means that each rerender of your component will use the same "parent" function, but that parent function still creates a new timer3
variable when invoked and returns a new inner function each time it is called.