useTimeout.tsx 1.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  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(() => {
  29. return cancel;
  30. }, [cancel]);
  31. return {
  32. /**
  33. * Start the timer
  34. *
  35. * If there was a previous timer, then it will be cancelled.
  36. */
  37. start,
  38. /**
  39. * Cancel the current timer
  40. *
  41. * Does not run the onTimeout callback.
  42. */
  43. cancel,
  44. /**
  45. * Stop the current timer
  46. *
  47. * Will run the onTimeout callback.
  48. */
  49. end,
  50. };
  51. }
  52. export default useTimeout;