I Am
Volodymyr Hudyma
<FrontEndDeveloper />
You Are Here: Home/Warning: Can't Perform A React State Update On An Unmounted Component

Warning: Can't Perform A React State Update On An Unmounted Component

December 02, 2020

Table Of Contents

    "Can't perform a React state update on an unmounted component" - is one of the most popular warnings React developers face.

    I am one hundred percent sure that every developer has encountered it at least once and did not know why it appeared and how to get rid of it.

    First of all, this warning is not critical, and seeing this does not necessarily mean that your application is not working as expected, but the warning should definitely not be omitted.

    The warning indicates that you have tried to set the state of the component that has already been unmounted.

    What Happens If I Omit It?

    The short answer is - it depends. It may or may not have a major impact on the performance of React applications.

    Assume that this warning is generated by the asynchronous call that waits for completion and sets the local state of the component:

    const User = () => {
      const [user, setUser] = useState<User | null>(null);
    
      useEffect(() => {
        setTimeout(() => {
          // The warning is generated by this code
          // If the component unmounts faster than 3s
          setUser({
            id: 1,
            name: "John",
          });
        }, 3000);
      }, []);
    
      return <div>{user?.name || "No user found"}</div>;
    };

    If only one component has missed handling the case of not setting the state after being unmounted, this has almost no effect on performance.

    However, if there are many such components (e.g. a list) and they are not all implemented correctly, the impact is enormous and the application is noticeably slowed down.

    Simple Example

    A simple example says more than a thousand words, so let's create a simple React application that outputs the warning (For better readability I will skip the styles, imports and exports).

    App.tsx:

    const App = () => {
      const [activeView, setActiveView] = useState("project");
    
      const toggleUser = () => {
        setActiveView("user");
      };
    
      const toggleProject = () => {
        setActiveView("project");
      };
    
      return (
        <Wrapper>
          <Button onClick={toggleProject}>Show project</Button>
          <Button onClick={toggleUser}>Show user</Button>
          {activeView === "user" ? <User /> : <Project />}
        </Wrapper>
      );
    };

    User.tsx:

    const User = () => {
      const [user, setUser] = useState<User | null>(null);
    
      useEffect(() => {
        setTimeout(() => {
          // The warning is generated by this code
          // If the component unmounts faster than 3s
          setUser({
            id: 1,
            name: "John",
          });
        }, 3000);
      }, []);
    
      return <div>{user?.name || "No user found"}</div>;
    };

    Project.tsx:

    const Project = () => <div>Project component</div>;

    The working app looks like this:

    Working React Application

    The buttons at the top act like the tabs, clicking on the first one displays the Project component, clicking on the second one - the User component.

    Now open the developer console and click on the "Show user" button and then again on "Show project":

    React Application With Warning

    If you manage to do this quickly enough (in less than 3 seconds), the warning will appear in the console.

    Why in less than 3 seconds? Because that is the timeout value we set for the User component, remember?

    useEffect(() => {
      setTimeout(() => {
        // The warning is generated by this code
        // If the component unmounts faster than 3s
        setUser({
          id: 1,
          name: "John",
        });
      }, 3000);
    }, []);

    When this component is rendered (after clicking the "Show user" button), the timeout is set, then the component is unmounted faster than the timeout ends, and we end up trying to run setState on an unmounted User component.

    Now we know exactly what the problem is, let's solve it.

    Fixing An Example

    The fix is quite simple - we need to declare a variable let mounted = true; inside the useEffect hook, set to false in the cleanup function, and check this variable before updating the state:

     useEffect(() => {
        // Set to "true" when component is mounted
        let mounted = true;
        setTimeout(() => {
          // Check if the component is still mounted
          if (mounted) {
            setUser({
              id: 1,
              name: "John",
            });
          }
        }, 3000);
        // Set to "false" when the component is unmounted
        return () => {
          mounted = false;
        };
      }, []);

    Summary

    The Warning: "Can't perform a React state update on an unmounted component" appears when the developer forgets to check whether the component is still mounted before updating its state.

    Although this warning is not a critical error in most cases, it is best to prepare a fix as soon as it appears to make sure that your application is working fine.

    Newsletter
    Receive all new posts directly to your e-mail
    No spam, only quality content twice a week
    Let me know what you think about this article
    Click here to write response...