All files / src/useTimeoutEffect useTimeoutOnEffect.ts

100% Statements 12/12
60% Branches 3/5
100% Functions 5/5
100% Lines 12/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62                                          2x   2x   65x 65x 63x   2x     2x 2x 2x 2x   2x                                           1x    
import { DependencyList, useEffect, useRef } from "react";
 
/**
 * This performs an operation where the timeout occurs at a given time, but limits the timeout so it
 * is recreated every specified interval.  On Android there is a limit of setTimeout to 60 seconds
 * which is the reason for this hook
 *
 * This hook wraps `setTimeout` to trigger an effect of invoking the callback function when the timeout hits.
 * This will clear the timeout if the component containing the hook is unmounted.  Unlike `setTimeout` this
 * does not support call back functions that accept arguments.  This is due to the need to pass the
 * useEffect dependency list.
 * @param callback - callback
 * @param on - when the timeout should fire
 * @param maxIntervalMs - maximum amount of time per interval.
 */
export function useTimeoutOnEffect(
  callback: () => void,
  on: Date,
  maxIntervalMs: number,
  deps: DependencyList = []
): void {
  const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
  /* eslint-disable react-hooks/exhaustive-deps */
  useEffect(() => {
    function doSetTimeout() {
      const timeRemaining = Math.max(0, on.getTime() - Date.now());
      if (timeRemaining > maxIntervalMs) {
        timeoutRef.current = setTimeout(doSetTimeout, maxIntervalMs);
      } else {
        timeoutRef.current = setTimeout(callback, timeRemaining);
      }
    }
    doSetTimeout();
    return () => {
      Eif (timeoutRef.current !== undefined) {
        clearTimeout(timeoutRef.current);
      }
      timeoutRef.current = undefined;
    };
  }, [maxIntervalMs, on, callback, ...deps]);
  /* eslint-enable react-hooks/exhaustive-deps */
}
 
/**
 * This performs an operation where the timeout occurs at a given time, but limits the timeout so it
 * is recreated every minute.  This is a convenience hook so `60000` need not be passed.
 *
 * This hook wraps `setTimeout` to trigger an effect of invoking the callback function when the timeout hits.
 * This will clear the timeout if the component containing the hook is unmounted.  Unlike `setTimeout` this
 * does not support call back functions that accept arguments.  This is due to the need to pass the
 * useEffect dependency list.
 * @param callback - callback
 * @param on - when the timeout should fire
 */
export function useTimeoutOnWithMinuteIntervalEffect(
  callback: () => void,
  on: Date,
  deps: DependencyList
): void {
  return useTimeoutOnEffect(callback, on, 60000, deps);
}