I'm building an app that displays images. Over top of each image, I wish to display an informative tooltip, but upon the mouse moving over top of the image, I want the tooltip to only display about 0.5 seconds after.
To this end, I have implemented this React Hook:
import * as React from 'react';
/**
* This is a React Hook implementation of the window.setTimeout function.
* Source: https://www.joshwcomeau.com/snippets/react-hooks/use-timeout
*
* @param {callback} onTimerDone
* @param {number} delay - milliseconds
*/
export const useTimeout = (onTimerDone: () => void, delay: number) => {
const timeoutRef = React.useRef(0);
const savedCallback = React.useRef(onTimerDone);
React.useEffect(() => {
savedCallback.current = onTimerDone;
}, [onTimerDone]);
React.useEffect(() => {
const tick = () => savedCallback.current();
timeoutRef.current = window.setTimeout(tick, delay);
return () => window.clearTimeout(timeoutRef.current); // Cleanup function
}, [delay]);
return timeoutRef;
};
I tried several approaches but can only get this timer hook to fire once. I'm looking for a timer that acts like a restartable timer. In other words, the timer will start counting down again & again whenever I instruct it to.
I can't figure out how to implement a Timeout hook that works in the way I need it to. Can anyone provide any suggestions?
If you need tooltip to show up after timeout you need to use event handler, not useEffect
.
// reusable hook to schedule timeouts
function useTimeoutTask() {
const timer = useRef<ReturnType<typeof setTimeout>>();
const cancel = useCallback(() => {
if (timer.current) {
clearTimeout(timer.current);
timer.current = undefined;
}
}, []);
const schedule = useCallback((handler: () => void, timeout: number) => {
// we only allow one active timeout at a time
cancel();
timer.current = setTimeout(handler, timeout);
}, []);
// Clear timeout on unmount
useEffect(() => () => cancel(), []);
return {
schedule,
cancel,
};
}
// and example of application
function useTooltip() {
const [showTooltip, setShowTooltip] = useState(false);
const { cancel, schedule } = useTimeoutTask();
const onMouseEnter = () => {
// setup task inside event handler
schedule(() => {
setShowTooltip(true);
}, 500);
};
const onMouseLeave = () => {
// cancel tooltip in case mouse leaves before tooltip was shown
cancel();
setShowTooltip(false);
};
}