useTimeout.tsx 1.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import {useCallback, useEffect, useRef} from 'react';
  2. type Options = {
  3. onTimeout: () => void;
  4. timeMs: number;
  5. };
  6. function useTimeout({timeMs, onTimeout}: Options) {
  7. const timeoutRef = useRef<number>(null);
  8. const saveTimeout = useCallback((timeout: ReturnType<typeof setTimeout> | null) => {
  9. if (timeoutRef.current) {
  10. clearTimeout(timeoutRef.current);
  11. }
  12. // See: https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables
  13. // @ts-expect-error
  14. timeoutRef.current = timeout;
  15. }, []);
  16. const start = useCallback(() => {
  17. saveTimeout(null);
  18. saveTimeout(setTimeout(onTimeout, timeMs));
  19. }, [onTimeout, saveTimeout, timeMs]);
  20. const cancel = useCallback(() => {
  21. saveTimeout(null);
  22. }, [saveTimeout]);
  23. const end = useCallback(() => {
  24. saveTimeout(null);
  25. onTimeout();
  26. }, [onTimeout, saveTimeout]);
  27. // Cancel the timeout on unmount
  28. useEffect(() => cancel, [cancel]);
  29. return {
  30. /**
  31. * Start the timer
  32. *
  33. * If there was a previous timer, then it will be cancelled.
  34. */
  35. start,
  36. /**
  37. * Cancel the current timer
  38. *
  39. * Does not run the onTimeout callback.
  40. */
  41. cancel,
  42. /**
  43. * Stop the current timer
  44. *
  45. * Will run the onTimeout callback.
  46. */
  47. end,
  48. };
  49. }
  50. export default useTimeout;