import {useCallback, useRef} from 'react'; type Options = { onTimeout: () => void; timeMs: number; }; function useTimeout({timeMs, onTimeout}: Options) { const timeoutRef = useRef<number>(null); const saveTimeout = useCallback((timeout: ReturnType<typeof setTimeout> | null) => { if (timeoutRef.current) { clearTimeout(timeoutRef.current); } // See: https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables // @ts-expect-error timeoutRef.current = timeout; }, []); return { /** * Start the timer * * If there was a previous timer, then it will be cancelled. */ start: useCallback(() => { saveTimeout(null); saveTimeout(setTimeout(onTimeout, timeMs)); }, [onTimeout, saveTimeout, timeMs]), /** * Cancel the current timer * * Does not run the onTimeout callback. */ cancel: useCallback(() => { saveTimeout(null); }, [saveTimeout]), /** * Stop the current timer * * Will run the onTimeout callback. */ end: useCallback(() => { saveTimeout(null); onTimeout(); }, [onTimeout, saveTimeout]), }; } export default useTimeout;