Using refs to check if a component is still mounted.
Tags: react, javascript, todayilearned, tutorial
Published: 2020-10-15 13:37:02

While reading [Learning React by Alex Banks and Eve Porcello](https://www.oreilly.com/library/view/learning-react-2nd/9781492051718/), I came across a very neat method to check if a component is still mounted or not.

This is useful because if you try to update the state of a component that is already unmounted you will get the following error.

![memory leak error](https://dev-to-uploads.s3.amazonaws.com/i/qolmv6mej46u50fnu666.png)

To show you what I just said, let's start by making a simple React application which only has a single **Card component** and a button which mounts and unmounts that **Card component**.

Here is **App.js**
```jsx
//App.js

import React, { useState } from "react";
import Card from "./Card";

export default function App() {
  const [showCard, setShowCard] = useState(true);

  const toggleCard = () => {
    setShowCard((prevState) => !prevState);
  };

  return (
    <>
      <button onClick={toggleCard}>Toggle Card</button>
      {showCard ? <Card /> : null}
    </>
  );
}
```
and **Card.js**
```jsx
//Card.js

import React, { useState } from "react";

export default function Card() {
  const [creature, setCreature] = useState("Unicorn");

  const changeCreature = (e) => {
    e.persist();
    setTimeout(() => {
      setCreature(e.target.value);
    }, 3000);
  };

  return (
    <div className="card">
      <button onClick={changeCreature} value="Unicorn">
        Unicorn
      </button>
      <button onClick={changeCreature} value="Phoenix">
        Phoenix
      </button>
      <h1 className="card__heading">
        All about {creature === "Unicorn" ? "Unicorns" : "Phoenixes"}
      </h1>
      <p className="card__description">
        {creature === "Unicorn"
          ? "Often considered the most wondrous of all mythical creatures, the unicorn is also a symbol of magic, miracles, and enchantment. The magical and enchanting unicorn appears to only a rare few and has the ability to bestow magic, miracles and wisdom to those who are pure of heart and virtuous in their deeds."
          : "A phoenix is a mythological bird that cyclically regenerates or is otherwise born again. Associated with fire and the sun, a phoenix obtains new life by arising from the ashes of its predecessor."}
      </p>
    </div>
  );
}
```
The content inside the Card component changes according to the state variable **creature** which can be changed using the two buttons **Unicorn** and **Phoenix**.

In the **App** component we have a state variable **showCard**, through which we can mount and unmount the **Card** component.

Please visit the [Live Demo](https://codesandbox.io/s/using-ref-blog-example-pc7it) to see what I am exactly talking about, *ignore the commented out code*.

## The Problem
Imagine we are getting the data about Unicorns and Phoenixes from an external API and it does actually take some time to retrieve that data if people are on a slow connection.

Here, I am trying to mimic the same behavior through a state change in **creature** which takes 3 seconds as I'm using the `setTimeout()` function which works asynchronously, implying that you can interact with the app throughout the `setTimeout()` duration which is exactly how asynchronous calls to an API work.

Now due to the slow connection, people may have a window of opportunity to press the Toggle Card button (which will unmount the **Card**) after they press any one of the Unicorn or Phoenix button which takes up 3 seconds to update the state (*try it yourself in the sandbox*).

This will reproduce the error I showed at the very beginning, the error says we are trying to update the state of a component that is already unmounted from our application which is exactly what we are doing, trying to update the state of our **Card** component which isn't mounted anymore.

## The Fix
💡 We will be using a ref to check if the component is still mounted or not before we update the state.

If it was a real API call what would happen is we would still make the API call as soon as the button (Unicorn or Phoenix) is clicked but just before we update the state with the data we fetched, we would use a ref inside a conditional to check if the component we are updating the state of still exists or if it unmounted.

Looking at the code will make things clearer.

```jsx
//Card.js

import React, { useEffect, useRef, useState } from "react";

export default function Card() {
  const [creature, setCreature] = useState("Unicorn");

  const mounted = useRef(false);

  useEffect(() => {
    mounted.current = true;
    return () => (mounted.current = false);
  });

  const changeCreature = (e) => {
    e.persist();
    setTimeout(() => {
      if (mounted.current) {
        setCreature(e.target.value);
      }
    }, 3000);
  };

  return (...);   //same as before
}
```

As you can see here that the `useEffect()` we added runs after every re-render (as it has no dependencies) and sets `mounted.current` to be `true` every time. The actual magic ✨ happens in the cleanup function (the function we return) which runs only when the component unmounts and changes `mounted.current` to `false`.

In the function where I update the state I've included an `if` check to see if the component is still mounted before calling `setCreature()`, the instance when the **Card** component is not mounted to the screen `mounted.current` will equate to `false` and the state (i.e. `creature`) will never be updated preventing the memory leak error.

Go to the same [Demo](https://codesandbox.io/s/using-ref-blog-example-pc7it) and uncomment the commented out code in **Card.js** to see for yourself.

## Why use refs?
We use refs here because *they're awesome* 🔮. Yeah, that's the only reason.

![The end gif](https://dev-to-uploads.s3.amazonaws.com/i/zcn2dzh98hcghpyk8lt0.gif)

Okay I am kidding.

>P.S. It's so hard to add humor, ughhh 😩. I know you must be shaking your head at the moment.

### So why did we use a ref here instead of something like a state variable `const [mounted, setMounted] = useState(false)`?

The answer is pretty simple, updating a ref never causes a re-render, whereas updating the state (i.e. using `setMounted()`) obviously does which will cause the `useEffect()` to run again and again causing an infinite loop.

## Taking it a step further
I see that checking if a component in mounted or not can be used at quite a lot of places so that is an opportunity to extract all of the logic inside a custom hook.

```jsx
//useMountedRef.js

import { useRef, useEffect } from 'react';

export default function useMountedRef() {
  const mounted = useRef(false);

  useEffect(() => {
    mounted.current = true;
    return () => (mounted.current = false);
  });

  return mounted;
}
```
Now it can be used anywhere as follows, `const mounted = useMountedRef();` and checking with `mounted.current` before updating the state will prevent the memory leak error.

*Remember: This is just a method of checking if a component is still mounted or not, the API request is still being made. So use it wisely if you can handle making that additional request.
Also, just use it anywhere where you feel the need for checking if a component is mounted.*

*Cover Illustration credits: [Lukasz Adam](https://lukaszadam.com/)*