# How to prevent React state update for asynchronous request on unmounted component?

**URL:** <https://forum.freecodecamp.org/t/how-to-prevent-react-state-update-for-asynchronous-request-on-unmounted-component/388023>\
**Category:** General\
**Created:** [May 13, 2020, 6:13am UTC](https://forum.freecodecamp.org/t/how-to-prevent-react-state-update-for-asynchronous-request-on-unmounted-component/388023 "2020-05-13T06:13:13Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![mav1283](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/mav1283/32/3706_2.png) [@mav1283](https://forum.freecodecamp.org/u/mav1283)\
**Post date:** [May 13, 2020, 6:13am UTC](https://forum.freecodecamp.org/t/how-to-prevent-react-state-update-for-asynchronous-request-on-unmounted-component/388023/1 "2020-05-13T06:13:13Z")

</div>

I’m working on a mernstack app where I have a custom hook for API requests with useReducer’s state and dispatch functions that is loaded into the context api.

Usually GET request runs smoothly on page load, but every time I use the POST, PATCH, PUT, and DELETE request functions it causes a component to unmount and 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._

The Main component displays the ParentList that is supplied with data from database on initial load through GET request inside useEffect simulating component did mount. I don’t know why it unmounts on event driven requests like post, patch, put and delete but the error goes away whenever I refresh the page and see the changes.

How to prevent React state update for asynchronous request on unmounted component?

**Custom Hook for API Requests:**

```auto
import { useEffect, useCallback, useReducer } from 'react';
import axios from 'axios';
import listReducer, { initialState } from '../../context/reducers/reducers';
import {
  loading,
  processingRequest,
  handlingError,
} from '../../context/reducers/actions/actionCreators';

const useApiReq = () => {
  const [state, dispatch] = useReducer(listReducer, initialState);

  const getRequest = useCallback(async () => {
    dispatch(loading());
    try {
      const response = await axios.get('/list');
      dispatch(processingRequest(response.data));
    } catch (err) {
      dispatch(handlingError);
    }
  }, []);

  const postRequest = useCallback(async (entry) => {
    dispatch(loading());
    try {
      const response = await axios.post('/list', entry);
      dispatch(processingRequest(response.data));
    } catch (err) {
      dispatch(handlingError);
    }
  }, []);

  const patchRequest = useCallback(async (id, updated_entry) => {
    dispatch(loading());
    try {
      const response = await axios.patch(`/list/${id}`, updated_entry);
      dispatch(processingRequest(response.data));
    } catch (err) {
      dispatch(handlingError);
    }
  }, []);

  const putRequest = useCallback(async (id, updated_entry) => {
    dispatch(loading());
    try {
      const response = await axios.put(`/list/${id}`, updated_entry);
      dispatch(processingRequest(response.data));
    } catch (err) {
      dispatch(handlingError);
    }
  }, []);

  const deleteRequest = useCallback(async (id) => {
    dispatch(loading());
    try {
      const response = await axios.delete(`/list/${id}`);
      dispatch(processingRequest(response.data));
    } catch (err) {
      dispatch(handlingError);
    }
  }, []);

  return [
    state,
    getRequest,
    postRequest,
    patchRequest,
    putRequest,
    deleteRequest,
  ];
};

export default useApiReq;

```

**Context API:**

```auto
import React, { createContext } from 'react';
import useApiReq from '../components/custom-hooks/useApiReq';

export const AppContext = createContext();

const AppContextProvider = (props) => {
  const [
    state,
    getRequest,
    postRequest,
    patchRequest,
    putRequest,
    deleteRequest,
  ] = useApiReq();

  return (
    <AppContext.Provider
      value={{
        state,
        getRequest,
        postRequest,
        patchRequest,
        putRequest,
        deleteRequest,
      }}
    >
      {props.children}
    </AppContext.Provider>
  );
};

export default AppContextProvider;

```

**App:**

```auto
import React from 'react';
import AppContextProvider from './context/AppContext';
import Header from './components/header/Header';
import Main from './components/main/Main';
import './stylesheets/styles.scss';

function App() {
  return (
    <AppContextProvider>
      <div className='App'>
        <Header />
        <Main />
      </div>
    </AppContextProvider>
  );
}
```

---

<div class="post-metadata">

**Author:** ![DanCouper](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/dancouper/32/158608_2.png) [@DanCouper](https://forum.freecodecamp.org/u/DanCouper)\
**Post date:** [May 13, 2020, 11:16am UTC](https://forum.freecodecamp.org/t/how-to-prevent-react-state-update-for-asynchronous-request-on-unmounted-component/388023/2 "2020-05-13T11:16:15Z")

</div>

The error message is telling you the problem. You are using useEffect, but you aren’t checking whether the thing it’s effecting is mounted or not, so what happens is that, due to the timing of the async stuff, at some point you are trying to apply changes to something that isn’t mounted. This won’t do much (as the error says, this is a no-op), but not doing anything may not be what you want, and what you’re doing is definitely causing a memory leak. I assume you see it on the non-GET requests because they are taking longer, but it could be for some other reason – I can’t run your code as there is no example so I don’t know.

At the top of the `useEffect` set a variable like `isMounted = true`. Only fire the request if `isMounted` is true (ie wrap the request in an if statemt). In the function that gets returned from useEffect, set `inMounted` to `false`, ie

```auto
return () => {
  isMounted = false
}

```

Ideally, use a ref instead of just a variable, that’s what they’re there for - use createRef somewhere in the relevant component that is using the useEffect/s, then set a it to true, then check `isMounted.current` for the value.

* * *

Just for future reference, this is a bit too much to expect people to read through. 90% of that code is just your app, it doesn’t have anything to do with the issue. It would be helpful to have the actual thing causing the problem isolated in ideally one component & function and put up somewhere (you can mock async requests by using a timeout instead of fetch)

Also, could just use Redux instead of trying to hand-roll it – you’ve written the same thing, but you’re missing all the tooling + a lot of optimisations here that you get out of the box with react-redux, and for basically the same amount of code

At the very least, IMO don’t put the state and the function in the same context provider, have one for state (read) and one for the dispatch (write), then write a hook for dispatching and for reading.

---

<div class="post-metadata">

**Author:** ![mav1283](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/mav1283/32/3706_2.png) [@mav1283](https://forum.freecodecamp.org/u/mav1283)\
**Post date:** [May 14, 2020, 2:59pm UTC](https://forum.freecodecamp.org/t/how-to-prevent-react-state-update-for-asynchronous-request-on-unmounted-component/388023/3 "2020-05-14T14:59:54Z")

</div>

@DanCouper Thanks for your inputs, I intentionally decided to use Context API + useReducer instead of Redux as I want to test its capability. Just a follow up question, i read on stackoverflow to use the axios cancel token, and i also read here: [https://www.robinwieruch.de/react-hooks-fetch-data](https://www.robinwieruch.de/react-hooks-fetch-data) where the author states: " _Since Axios Cancellation has not the best API in my eyes, this boolean flag to prevent setting state does the job as well._ - Your suggestion match with his approach. In your opinion which is the most ideal, using a variable or axios cancel token? Thanks again!

---

<div class="post-metadata">

**Author:** ![DanCouper](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/dancouper/32/158608_2.png) [@DanCouper](https://forum.freecodecamp.org/u/DanCouper)\
**Post date:** [May 14, 2020, 6:45pm UTC](https://forum.freecodecamp.org/t/how-to-prevent-react-state-update-for-asynchronous-request-on-unmounted-component/388023/4 "2020-05-14T18:45:44Z")

</div>

The token prevents the action on an unmounted component, but it doesn’t cancel the fetch request if it’s already been started. If it is important that a request is stopped, then you would use the cancellation API (or an AbortController if you were using fetch directly rather than Axios). If it doesn’t matter (normally doesn’t), then you can avoid the extra complexity and just use a token.

---

<div class="post-metadata">

**Author:** ![mav1283](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/mav1283/32/3706_2.png) [@mav1283](https://forum.freecodecamp.org/u/mav1283)\
**Post date:** [May 16, 2020, 3:44pm UTC](https://forum.freecodecamp.org/t/how-to-prevent-react-state-update-for-asynchronous-request-on-unmounted-component/388023/5 "2020-05-16T15:44:09Z")

</div>

It tried Axios’s cancel token but i get the same warning, here’s my updated custom hook:

 ![custom-hook](https://global.discourse-cdn.com/freecodecamp/original/3X/6/8/681e91327c13b6690d0781b34742f8b06c585932.png)

---

<div class="post-metadata">

**Author:** ![ILM](https://sea3.discourse-cdn.com/freecodecamp/user_avatar/forum.freecodecamp.org/ilm/32/363239_2.png) [@ILM](https://forum.freecodecamp.org/u/ILM)\
**Post date:** [June 1, 2021, 6:53pm UTC](https://forum.freecodecamp.org/t/how-to-prevent-react-state-update-for-asynchronous-request-on-unmounted-component/388023/6 "2021-06-01T18:53:32Z")

</div>

Closed on June 1, 2021, 6:53pm UTC


