All files / src/usePolling usePollingIf.ts

100% Statements 17/17
84.61% Branches 11/13
100% Functions 4/4
100% Lines 17/17

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                              18x         18x           18x       18x     45x 45x 45x 42x 42x   3x     45x   45x 44x       18x 18x 18x 18x        
import { useEffect } from "react";
import { defaultPollingOptions } from "./defaultPollingOptions";
import { PollingOptions } from "./PollingOptions";
 
/**
 * This performs polling while a predicate returns true.
 * @param predicate - an async function that if false will skip the callback, but will still poll.
 * @param asyncFunction - the async function to call.
 * @param options - extra options for polling
 */
export function usePollingIf<T = unknown>(
  predicate: () => boolean | PromiseLike<boolean>,
  asyncFunction: () => T | PromiseLike<T>,
  options: Partial<PollingOptions> = {}
): void {
  const { intervalMs, immediate, onError } = {
    ...defaultPollingOptions,
    ...options,
  };
 
  useEffect(() => {
    let timeoutID: ReturnType<typeof setTimeout>;
    /*
     * Flag to indicate that the async function is still actively running.  If it is actively running it skips
     * execution and delays it until the next tick.
     */
    let active = false;
    /*
     * Flag to indicate that the cleanup was invoked so no more executions should be performed.
     */
    let cleanupCalled = false;
    // the function is built here rather than on the top level so the timeout variable is managed within this function.
    async function wrappedAsyncFunction(): Promise<void> {
      Eif (!active && !cleanupCalled) {
        active = true;
        if (await predicate()) {
          try {
            await asyncFunction();
          } catch (e) {
            onError(e);
          }
        }
        active = false;
      }
      if (!cleanupCalled) {
        timeoutID = setTimeout(wrappedAsyncFunction, active ? 0 : intervalMs);
      }
    }
 
    timeoutID = setTimeout(wrappedAsyncFunction, immediate ? 0 : intervalMs);
    return () => {
      clearTimeout(timeoutID);
      cleanupCalled = true;
    };
  }, [immediate, intervalMs, asyncFunction, onError, predicate]);
}