Nans' blogs

An elegant solution for memory leaks in React

July 30, 2020

🔴 UPDATE

This “solution” doesn’t seem to really avoid leaks. Even AbortController doesn’t seem to be the silver bullet against memory leaks 😰. Check out the discussion in the comments!


When working with asynchronous calls, for example API calls, you might have encountered this error :

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.

A GIF is worth a thousand words …

leaky GIF

This is a small page that simulates some asynchronous logic on load, and then updates the view accordingly. Here, I unmount the component before the async work has been finished, and trigger the Error. (I took this example from this StackOverFlow post)

This is caused by this code :

function Example() {
  const [text, setText] = useState("waiting...");

  useEffect(() => {
    simulateSlowNetworkRequest().then(() => {
      setText("done!"); // ⚠️ what if the component is no longer mounted ?
      // => Warning: Can't perform a React state update on an unmounted component.
    });
  }, []);

  return <h2>{text}</h2>;
}

When running into that issue, I found multiple solutions, the most used one seems to be this one :

function OtherExample() {
	const [text, setText] = useState("waiting...");

  useEffect(() => {
    let isMounted = true; // 👈
    simulateSlowNetworkRequest().then(() => {
      if (!isMounted) { // 👈
        setText("done!"); // no more error
      }
    });
    return () => {
      isMounted = false; // 👈
    };
  }, []);

  return <h2>{text}</h2>;
}

But it requires you to add quite a lot of stuff into your component, having to deal with that isMounted variable all over the place …

There are other interesting solutions, like making your Promises cancellable.

 You told me there would be an elegant solution !

I wasn’t lying! The solution I came up with is a very simple hook. It works just like React’s useState, but it basically checks if the component is mounted before updating the state !

Here is an example of the refactored code :

function OtherExample() {
  const [text, setText] = useStateIfMounted("waiting..."); // 👈

  React.useEffect(() => {
    simulateSlowNetworkRequest().then(() => {
      setText("done!"); // no more error
    });
  }, [setText]);

  return <h2>{text}</h2>;
}

Here is the CodeSandBox if you wanna play around !

TLDR

Use useStateIfMounted hook, that will only update the state if your component is mounted ! 🚀

I hope this might be helpful, feel free to reach me out in any case ! 🤗


Written by Nans Dumortier who is a French Software Developer.
Find me on :