DEV Community

Discussion on: Clean Up Async Requests in `useEffect` Hooks

Collapse
 
pallymore profile image
Yurui Zhang • Edited

In your case using the flag is not going to work.
If you are using fetch to make requests you can use AbortController (just provide the signal to the thunk action)

useEffect(() => {
   const abortController = new AbortController();

  dispatch(thunkAction({ abortSignal: abortController.signal })); // provide abortSignal to `fetch` in your middeware;

  return () => {
    abortController.abort();
  };
}, [...]);

in your request handler's catch block, make sure checking if the error is an "AbortError"

  fetch(..., { signal: abortSignal })
   .then(/* set state with redux */)
   .catch((e) => {
      if (e.name !== 'AbortError') {
         // request failed
      } else {
         // request was cancelled.
      }
   });

There are many different implementations for making requests with thunk actions - could you show some code?

Collapse
 
smritispotnana profile image
smriti-spotnana • Edited

my component file has this -

useEffect((): void => {
    const params = qs.unstringify(search);
    dispatch(thunk(params));
  }, [dispatch, search]);

in a diff .js file, I have written the thunk function. which uses axios internally -

import api from "./api";
export const results = params => async (dispatch) => {
  try {
   const { data } = await api("POST", "search", { data });
   dispatch(updateReduxState(data);
   }
 catch {
   dispatch(failure(false));
  };
};
Thread Thread
 
pallymore profile image
Yurui Zhang

Looks like you can just introduce a second parameter to your thunk function:

// thunk function
export const thunk = async (params, cancelToken) => {
  try {
    const { data } = await api('POST', 'search', { data, cancelToken });
  }
  catch(e) {
    if (!axios.isCancel(error)) {
       // not cancelled  dispatch failure action
    } else {
       // canceled - handle it or ignore it
    }
  }
}

now in your component you should provide the cancel token to thunk

useEffect(() => {
  const source = axios.CancelToken.source();
  dispatch(thunk(params, source.token));
  return () => {
    source.cancel(); // cancel previous request when effect fires again, or when component unmounts
  };
});
Thread Thread
 
smritispotnana profile image
smriti-spotnana

so, inside my api function, I am creating new token on every request - if it's the same request though, then it cancels the prev token/req, and re-generate the token and handling everything related to axios inside this function.
So, I will need to change it and basically create token inside useEffect.
Will you be able to redirect me to React docs/github/etc where they suggest this solution? Thank you

Thread Thread
 
pallymore profile image
Yurui Zhang

No, the token should be created in your useEffect call. A new token is created for every new "effect". cancel / abort is called whenever the effect re-fires (e.g. when the parameters changed, or when the component unmounts), the cleanup function is called, cancelling the previous request - in your API function you should check if a request has been aborted in your catch block and handle it accordingly.

some helpful articles:
reactjs.org/blog/2015/12/16/ismoun...
reactjs.org/docs/hooks-effect.html
github.com/axios/axios#cancellation