Search code examples
javascriptreactjsecmascript-6react-hooksfetch

fetch calls in react world: ReactJS


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, extra = {}) => request({ url, type: "GET", ...extra });
const post = (url, payload, extra = {}) => request({ url, data: payload ,type: "POST",
}, ...extra });

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?


Solution

  • 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;
      }
    }, []);