Home > OS >  Cancelling the fetch calls in react world: ReactJS
Cancelling the fetch calls in react world: ReactJS

Time:03-31

There is a requirement of cancelling the request calls when navigating away from the page or when the same api call is made multiple calls ( keeping the last one active).

This is how the API is extracted out( just a high level)

AJAX.ts

export async function customAjax(options){
   let options = {};
   options.headers = { ...options.headers, ...obj.headers };
   const response = await fetch(url, options);
   await response.json()
}

GET and POST calls are being extracted as

API.ts

const get = (url, additional = {}) => request({ url, type: "GET", ...additional });
const post = (url, payload, additional = {}) => request({ url, data: payload ,type: "POST",
}, ...additional });

const request = async (payload) => {
      try {
          promise = customAjax(payload);
          const response = await promise;
          const errorMessage = _get(response, "error", false) || _get(response, "errorCode", false);
          if (response && !errorMessage) {
            return { response, error: false, loading: false, request: promise };
          } else if (errorMessage) {
            return { response: false, error: errorMessage, loading: false, request: promise };
          }
      } 
     catch (e) {
         const errorMessage = typeof e === "object" ? _get(e, "message") || _get(e, "error") : e;
         return { response: false, error: errorMessage, loading: false, request: promise };
     }
    return { response: false, error: false, loading: true, request: promise };
}

In the react component I call these utilities as follows:

function MyComponent(){
  useEffect(() => {
    makeCall();
  }, []);

 async function makeCall(){
   const { response, error } = await API.post(URL, payload);
   // Handling code is not added here
   // In the similar fashion GET calls are also made
 }
}

I have come across Abortcontroller to cancel request where we could use abort method during unmounting of the component.

Is there a way to do this at a utililty level, may be inside customAjax so that I could avoid writing abort controller code everywhere?

CodePudding user response:

From my understanding... What you describe is no different than a memory leak issue. And the current method for avoiding memory leaks is with the AbortController().

As far as handling this at the "utility level", I don't think this is feasible, and indeed would go against the preferred notion of an api being unaware of what's going on at the React component level; i.e separation of concerns..

So, in order to accomplish your requirement, you'll need to use AbortController(), or a custom implementation using a boolean flag that reflects whether the component is mounted, on a per component basis.

Using the boolean flag, you may be able to accept an argument in your api, passing the flag as a parameter; but again, I think this would be considered an anti-pattern.

I understand you're looking for a minimal implementation; but standard practice is fairly minimal:

useEffect(() => {
   let abortController = new AbortController();
    // Async code
   return () => { abortController.abort(); }
}, []);

Using a boolean flag would be more verbose, and would entail something like this in your case:

useEffect(() => {
  let isMounted = true;
  
  customAjax(isMounted);

  return () => {
   isMounted = false;
  }
}, []);

CodePudding user response:

To handle out-of-order ajax responses, you can use a local variable inside the effect. For example,

useEffect(() => {
    let ignore = false;
    async function fetchProduct() {
      const response = await fetch('http://myapi/product/'   productId);
      const json = await response.json();
      if (!ignore) setProduct(json);
    }

    fetchProduct();
    return () => { ignore = true };
}, [productId]);

The ignore variable will ensure that only the latest request's response is updated to state. Reference - https://reactjs.org/docs/hooks-faq.html#performance-optimizations

Regarding memory leak concerns, please see this discussion - https://github.com/reactwg/react-18/discussions/82

  • Related