DEV Community

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

Collapse
 
ffigueroa2803 profile image
Frankie Figueroa Muñoz • Edited

Could you help me how to cancel request using userMeno with dispatch I get this error ( Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. )

const [stateApp, dispatch] = useReducer(
(stateApp, action) => {
switch (action.type) {
case "SET_APP":
return {
...stateApp,
data: { ...action.payload },
loading: false,
success:true,
error: undefined
};
case "SET_ERROR":
return {
...stateApp,
error: { ...action.payload },
loading:false,
data: {},
success:false
};
default:
return stateApp
}
},
{
data: {},
error: undefined,
loading: true,
success: false
},
)

const app = useMemo(() => ({
    setting: async () => {
        await authentication.get("app/me").then( response => {
            let { success, message, app } = response.data
            if (!success) throw new Error(message)
            dispatch(createAction("SET_APP", app))
        }).catch(async error => {
            await LOGOUT()
            dispatch(createAction("SET_ERROR", error))
        })
    }
}), [])
Enter fullscreen mode Exit fullscreen mode
Collapse
 
pallymore profile image
Yurui Zhang

Hi - I'd recommend not to use useMemo in this case - it should be reserved for memoizing results from expensive operations - not functions! useMemo should be avoided unless you absolutely need it for performance reasons (in real life this doesn't happen very often).

Back to the topic - in your case your method is not cancelled when the component unmounts - the right solution would be creating a cancel token (or AbortController if you are using fetch ) and provide the signal to your authentication.get method - and then in catch make sure you check for AbortErrors. This depends on the implementation of your authentication.get method.

Alternatively - you can do something like this:

const abortController = useRef(new AbortController());

useEffect(() => () => abortController.current.abort(), []); // aborts when the hook unmounts.

// then later in your then/catch blocks:
// ...
async () => {
  await authentication.get('app/me').then(r => {
    if (abortController.current.signal.aborted) return; // ignore the rest of the code if signal is aborted
    // update state
  }).catch(e => {
    if (abortController.current.signal.aborted) return; // same ignore the rest of the code if signal is aborted
    // update state
  })
}
Enter fullscreen mode Exit fullscreen mode

please note the request is not aborted unless you provide the signal to your fetch call. this code here only prevents react from performing state updates.