A custom hooks to use an async effect

Published on
2 mins read
10 views

Custom hooks to use an async effect

function useAsync(asyncCallback) {
  let [state, dispatch] = React.useReducer(asyncReducer);

  React.useEffect(() => {
    let promise = asyncCallback();
    if (!promise) return;

    dispatch({ type: "pending" });
    promise
      .then((data) => dispatch({ type: "resolved", data }))
      .catch((error) => dispatch({ type: "rejected", error }));
  }, [asyncCallback]);

  return state;
}

Usage:

function Component({ input }) {
  // Remember to wrap the async job in a useCallback
  let asyncCallback = React.useCallback(() => {
    if (!input) return;

    // Run the async effect (fetch is an example)
    return fetch(input);
  }, [input]);

  let { status, data, error } = useAsync(asyncCallback);

  switch (status) {
    case "idle":
      return "Waiting for the async to trigger";
    case "pending":
      return "Pending UI";
    case "rejected":
      throw error;
    case "resolved":
      return "Data UI";
    default:
      throw new Error("This should be impossible");
  }
}

How to clean the side effect (the async job start but then the component unmounted) ? - useSafeDispatch !

function useSafeDispatch(dispatch) {
  let mountedRef = React.useRef(false);
  React.useEffect(() => {
    mountedRef.current = true;
    return () => {
      mountedRef.current = false;
    };
  }, []);

  return React.useCallback(
    (...args) => {
      if (mountedRef.current) {
        dispatch(...args);
      }
    },
    [dispatch]
  );
}

Now change the useAsync function:

function useAsync(asyncCallback) {
  let [state, unsafeDispatch] = React.useReducer(asyncReducer);
  let dispatch = useSafeDispatch(unsafeDispatch);

  React.useEffect(() => {
    let promise = asyncCallback();
    if (!promise) return;

    dispatch({ type: "pending" });
    promise
      .then((data) => dispatch({ type: "resolved", data }))
      .catch((error) => dispatch({ type: "rejected", error }));
  }, [asyncCallback]);

  return state;
}

Cheers