1

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?

1
  • When you navigate away form a page in the browser, all resources associated with that page are released automatically. This would include sockets associated with fetch() calls that are in progress. You don't have to do anything in your own code to cause that to happen. Now, if you're not actually changing the top level URL in the browser and are just updating the content in the current web page, then that's a different beast as the browser doesn't handle any of that for you. Commented Mar 30, 2022 at 19:26

2 Answers 2

0

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;
  }
}, []);
Sign up to request clarification or add additional context in comments.

7 Comments

What would be the minimal way of doing this , if going by a component level? Like may be custom hook, where in abortcontroller is instantiated and stuff, How would you achieve uit with minimal change?
Using AbortController in the useEffect method, you can accomplish this with 2 lines of code.. That's pretty minimal.
According to your approach, what should customAjax do with the flag passed?
@li97 simply wrap your fetch call, or other async operation, in a condition checking whether the flag is true or false...
thanks for answer, still having trouble understanding the flag way.
|
0

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

1 Comment

Needing a minimal code change approach, with the current code extracton that is mentioned in the question with Abortcontroller

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.