All files / src/useAsyncSetEffect useAsyncSetEffect.ts

100% Statements 6/6
100% Branches 3/3
100% Functions 3/3
100% Lines 6/6

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                                            9x     9x 7x 7x 6x 5x            
import { DependencyList, EffectCallback, useEffect } from "react";
 
import { useMounted } from "../useMounted";
 
/**
 * This starts an async function and executes another function that performs
 * React state changes if the component is still mounted after the `async`
 * operation completes.  This uses {@link useMounted} for handling the
 * mount logic.
 * @param asyncFunction - async function,
 *   it has a copy of the mounted ref so an await chain can be canceled earlier.
 *   this should ideally be wrapped with useCallback to prevent rerenders
 * @param onSuccess - this gets executed after async
 *   function is resolved and the component is still mounted
 *   this should ideally be wrapped with useCallback to prevent rerenders
 * @param deps - dependency list
 */
export function useAsyncSetEffect<T>(
  asyncFunction: () => Promise<T>,
  onSuccess: (asyncResult: T) => void,
  deps: DependencyList = []
): void {
  const isMounted = useMounted();
  // eslint is disabled since the deps are needed in this csae
  /* eslint-disable react-hooks/exhaustive-deps */
  useEffect((): ReturnType<EffectCallback> => {
    (async function wrapped() {
      const asyncResult = await asyncFunction();
      if (isMounted()) {
        onSuccess(asyncResult);
      }
    })();
  }, [asyncFunction, isMounted, onSuccess, ...deps]);
  /* eslint-enable react-hooks/exhaustive-deps */
}