In React, we use setState to update the state of our components. While it is rare to encounter issues while using setState, we might run into some warnings when we use it in an asynchronous operation. In this post, we will learn how to handle React State Update on Unmounted Component.

A few situations when the React State Update warning can show up is as follows:

  • Calling setState within a function that returns a promise. In this scenario, we call setState within the .then, .finally or .catch blocks of the promise. However, when the component linked to the promise gets destroyed before the promise is actually resolved, it can lead to this warning.
  • We can also see this warning when we use a router such as React router. If the route changes while the piece of asynchronous code with the setState call is running, it will cause the warning to popup.

Before we go further, let’s re-create the issue.

1 – React State Update Warning

The React state update warning is fairly easy to re-create.

We will create a simple component as below:

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

function SetStateWarning() {
    const [ message, setMessage ] = useState("Starting Message");

    useEffect(() => {
        setTimeout(() => setMessage("New Message"), 3000)
    })

    return (
        <div>
            <p>{message}</p>
        </div>
    )
}

export default SetStateWarning;

In this component, we display a message. The message is part of the component state. When the component initializes, the value of the message is ‘Starting Message’. However, we change the message to ‘New Message’ after 3 seconds. This is an asynchronous operation.

Basically, we are using useState React Hook to handle the initial state and useEffect React Hook to set the state to a new value.

We can now use this component as below:

import React, {useState} from 'react';
import SetStateFixed from './SetStateFixed';
import SetStateWarning from './SetStateWarning';

function StateUpdateDemoComponent() {
    const [ showExampleComponent, setShowExampleComponent ] = useState(true);
    
    const clickHandler = event => {
        event.preventDefault();
        setShowExampleComponent(!showExampleComponent);
    }

    return (
        <div>
            <p>Calling Set State on Unmounted Component</p>

            {showExampleComponent && <SetStateWarning />}
            <button onClick={clickHandler}>Toggle Component</button>
        </div>
    )
}

export default StateUpdateDemoComponent;

In the above component, we can toggle our SetStateWarning component on or off by clicking the Toggle Component button. If we toggle off the SetStateWarning component by clicking the button before the asynchronous function within the component completes (in other words, before 3 seconds), we will get the below warning in the console.

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.
    at SetStateWarning (http://localhost:3000/static/js/bundle.js:639:80)

This warning is pointing out that we can’t perform a React state update on an unmounted component. Basically, by the time the setState function within the component was triggered, the component was unmounted from the DOM.

While this warning does not break our application, it is still relatively serious. As the description suggests, it can lead to memory leaks in our application.

2 – React State Update Warning Solution

Now that we understand why the warning appears, it is time to ask the important question.

How do we solve the React State Update Warning?

To solve the React State Update Warning on an Unmounted Component, we need to avoid updating state asynchronously in a component that might get unmounted from the DOM. In other words, we need to manage the state for such components in a different manner. The best way to achieve this is to lift the state to the parent component.

Let us see how we can achieve the same.

We will first create a new component to display the message as below.

function SetStateFixed({message} = props) {
    return (
        <div>
          <p>{message}</p>
        </div>
      );
}

export default SetStateFixed;

As you can see, we are no longer managing the message variable within the component state. Instead, we receive the message as part of the props.

Now, we can use this component as below:

import React, {useState, useEffect} from 'react';
import SetStateFixed from './SetStateFixed';

function StateUpdateDemoComponent() {
    const [ showExampleComponent, setShowExampleComponent ] = useState(true);
    const [ message, setMessage ] = useState("Starting Message");
    
    const clickHandler = event => {
        event.preventDefault();
        setShowExampleComponent(!showExampleComponent);
    }

    useEffect(() => {
        setTimeout(() => setMessage("New text"), 3000);
    });

    return (
        <div>
            <p>Calling Set State on Unmounted Component</p>

            {showExampleComponent && <SetStateFixed message={message} />}
            <button onClick={clickHandler}>Toggle Component</button>
        </div>
    )
}

export default StateUpdateDemoComponent;

We are now handling the message as part of this component’s state. We also moved the setMessage() call within the StateUpdateDemoComponent.

Once the component is initialized, the message gets updated after 3 seconds. However, if we toggle the SetStateFixed component and unmount it from the DOM, it does not cause the React State Update Warning. This is because we have shifted the setState call from a component that may get unmounted to a component that won’t get unmounted.

Conclusion

With this, we have successfully learnt how to handle React State Update Warning on an unmounted component.

We achieved the same by managing our state properly so that we don’t execute setState on a component that can get potentially unmounted.

Want to learn more about handling errors in React? Check out this post on React Error Boundary.

If you have any comments or queries about this post, please feel free to mention them in the comments section below.

Categories: React

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *