I have a button that has a callback function. I want the button to start a timeout with a 5 second delay when clicked. If the button is clicked again within that 5 seconds, I want the timeout to reset without calling the timeout handler. Then when 5 seconds elapses, the handler is called.
[timeoutHasInitiated, setTimeoutHasInitiated] = useState(false);
function callbackFunction() {
if (!timeoutHasInitiated) {
setTimeoutHasInitiated(true);
console.log('Button clicked: Timeout Started.');
const timeout = setTimeout(() => {
setTimeoutHasInitiated(false);
console.log('Timeout has Finished.');
}, 5000);
return () => clearTimeout(timeout);
}
else {
console.log('Button clicked while timeout in progress');
}
}
In the code above, clicking the button after the timeout is in progress does nothing.
If I try something like:
function callbackFunction() {
clearTimeout(timeout);
const timeout = setTimeout(() => {
setTimeoutHasInitiated(false);
console.log('Timeout has Finished.');
}, 5000);
return () => clearTimeout(timeout);
}
I get an error:
Block-scoped variable ‘timeout’ used before its declaration.
Variable ‘timeout’ used before it is assigned.
If I try to declare the timeout first before assigning it to setTimeout() like so:
function callbackFunction() {
let timeout;
clearTimeout(timeout);
timeout = setTimeout(() => {
setTimeoutHasInitiated(false);
console.log('Timeout has Finished.');
}, 5000);
return () => clearTimeout(timeout);
}
The clearTimeout(timeout) called before setTimeout() doesn’t seem to do anything, as multiple timeouts get initiated from multiple button presses.
I suppose I could just disable the button after it is clicked as a workaround, but I was wondering if there was a way to program the desired behavior I described.
2
Answers
If you move your declaration of
timeout
outside ofcallbackFunction
, it should work as you intend.Each call to
callbackFunction
creates a new execution context so currently each call is accessing its own locally scoped declaration oftimeout
, which is why you’re seeing multiple timeouts initiate from multiple button presses. Or you could scopetimeout
to the function by usingthis.timeout
, but being self-taught I’m not sure of the downsides of such an approach.First, you’ll need to move the timeout outside the function like this:
Second, you’ll need to change your function like this:
And finally, you’ll need to globally clear the timeout, like this: