useMountedEffect: asynchronous useEffect on potentially unmounted components
Tags: react, typescript, reacthooks
Published: 2021-07-11 13:05:19

---
title: useMountedEffect: asynchronous useEffect on potentially unmounted components
published: true
description: this post describes how to use asynchronous logic within effects of a react app without causing issues after unmounting 
tags: react, typescript, reacthooks
//cover_image: https://direct_url_to_image.jpg
---

When we use effects, or more precisely the `useEffect` hook, than because very often we do want to execute something asynchronously. In most scenarios we are doing this when a component gets mounted. 

### Problem

1. Component gets mounted
2. useEffect gets executed
3. we set the state that we are starting to load something
4. we fetch data from the server
5. ups... the user navigated away
6. our component gets unmounted
7. our async function resolves finally
8. happy that we got data from server, we call setState to update our component
9. error: component shouts at us because we set state on something that does not exist anymore

### Solution

So what is the solution here? Well, React tells us already, when it unmounted the component. We just need to listen to what React tells us, but unfortunately it is not that straight forward and looks also a little bit weird. 

But because every hook can ask for a callback for the event of unmounting, we can abstract the handling of state whether we are mounted or not into a custom hook. 

### useMountedEffect

this hook basically is the same as the `useEffect` but has a 3 different points. 

1. it can be asynchronous
2. the callback passed in will receive a `isMounted` function
3. you cannot return a unmount funciton by yourself unfortunately

#### Here is how it looks

{% gist https://gist.github.com/theluk/e748734d84031c3aa85af469d63dcedc file=useMountedEffect.ts %}

#### So What happens here?

1. We define the arguments of the hook.

It is very similar to the original `useEffect`. It has the dependency array, but the callback you pass in, will receive a `isMounted` function. You can call this function whenever you want to check if the component is mounted or not. 

2. We create two `Ref`s using `useRef`

I use ref's for everything that does not require reactivity and if I am only interested in the latest version of some value. 

Here we do not want to "react" if your callback changes, or if we get unmounted/mounted. We want to **only** react to changes, if the dependency array changes. 

3. The hook stores the current state whether it is mounted or not. To get this state, we define a function that resolves with the mounted reference. 

4. The hook calls the current callback. The callback can change but we call it only if the dependency array changes, so it works basically exactly like useEffect. 

5. the callback now should check whether it should set a state based on the information `isMounted()` returns. 

### Example

```js
const MyComponent = ({ id }) => {
  const [status, setStatus] = useState();
  const [data, setData] = useState();
  
  useMountedEffect(async (isMounted) => {
    setStatus("LOADIGN");
    try {
      const data = await fetchMyData({ id });
      // only set state if we are still mounted
      if (isMounted()) {
        setData(data);
        setStatus("DONE")
      }
    } catch (e) {
      if (isMounted()) {
        setStatus("ERROR")
      }
    }
  }, [id])
}
```