javascript - Can't perform a React state update on an unmounted component

ID : 10014

viewed : 30

Tags : javascriptreactjstypescriptlodashsetstatejavascript

Top 5 Answer for javascript - Can't perform a React state update on an unmounted component

vote vote

100

Here is a React Hooks specific solution for

Error

Warning: Can't perform a React state update on an unmounted component.

Solution

You can declare let isMounted = true inside useEffect, which will be changed in the cleanup callback, as soon as the component is unmounted. Before state updates, you now check this variable conditionally:

useEffect(() => {   let isMounted = true;               // note mutable flag   someAsyncOperation().then(data => {     if (isMounted) setState(data);    // add conditional check   })   return () => { isMounted = false }; // cleanup toggles value, if unmounted }, []);                               // adjust dependencies to your needs 

const Parent = () => {   const [mounted, setMounted] = useState(true);   return (     <div>       Parent:       <button onClick={() => setMounted(!mounted)}>         {mounted ? "Unmount" : "Mount"} Child       </button>       {mounted && <Child />}       <p>         Unmount Child, while it is still loading. It won't set state later on,         so no error is triggered.       </p>     </div>   ); };  const Child = () => {   const [state, setState] = useState("loading (4 sec)...");   useEffect(() => {     let isMounted = true;     fetchData();     return () => {       isMounted = false;     };      // simulate some Web API fetching     function fetchData() {       setTimeout(() => {         // drop "if (isMounted)" to trigger error again          // (take IDE, doesn't work with stack snippet)         if (isMounted) setState("data fetched")         else console.log("aborted setState on unmounted component")       }, 4000);     }   }, []);    return <div>Child: {state}</div>; };  ReactDOM.render(<Parent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <div id="root"></div> <script>var { useReducer, useEffect, useState, useRef } = React</script>

Extension: Custom useAsync Hook

We can encapsulate all the boilerplate into a custom Hook, that automatically aborts async functions in case the component unmounts or dependency values have changed before:

function useAsync(asyncFn, onSuccess) {   useEffect(() => {     let isActive = true;     asyncFn().then(data => {       if (isActive) onSuccess(data);     });     return () => { isActive = false };   }, [asyncFn, onSuccess]); } 

// custom Hook for automatic abortion on unmount or dependency change // You might add onFailure for promise errors as well. function useAsync(asyncFn, onSuccess) {   useEffect(() => {     let isActive = true;     asyncFn().then(data => {       if (isActive) onSuccess(data)       else console.log("aborted setState on unmounted component")     });     return () => {       isActive = false;     };   }, [asyncFn, onSuccess]); }  const Child = () => {   const [state, setState] = useState("loading (4 sec)...");   useAsync(simulateFetchData, setState);   return <div>Child: {state}</div>; };  const Parent = () => {   const [mounted, setMounted] = useState(true);   return (     <div>       Parent:       <button onClick={() => setMounted(!mounted)}>         {mounted ? "Unmount" : "Mount"} Child       </button>       {mounted && <Child />}       <p>         Unmount Child, while it is still loading. It won't set state later on,         so no error is triggered.       </p>     </div>   ); };  const simulateFetchData = () => new Promise(   resolve => setTimeout(() => resolve("data fetched"), 4000));  ReactDOM.render(<Parent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js" integrity="sha256-32Gmw5rBDXyMjg/73FgpukoTZdMrxuYW7tj8adbN8z4=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js" integrity="sha256-bjQ42ac3EN0GqK40pC9gGi/YixvKyZ24qMP/9HiGW7w=" crossorigin="anonymous"></script> <div id="root"></div> <script>var { useReducer, useEffect, useState, useRef } = React</script>

More on effect cleanups: Overreacted: A Complete Guide to useEffect

vote vote

88

To remove - Can't perform a React state update on an unmounted component warning, use componentDidMount method under a condition and make false that condition on componentWillUnmount method. For example : -

class Home extends Component {   _isMounted = false;    constructor(props) {     super(props);      this.state = {       news: [],     };   }    componentDidMount() {     this._isMounted = true;      ajaxVar       .get('https://domain')       .then(result => {         if (this._isMounted) {           this.setState({             news: result.data.hits,           });         }       });   }    componentWillUnmount() {     this._isMounted = false;   }    render() {     ...   } } 
vote vote

79

If above solutions dont work, try this and it works for me:

componentWillUnmount() {     // fix Warning: Can't perform a React state update on an unmounted component     this.setState = (state,callback)=>{         return;     }; } 
vote vote

61

There is a hook that's fairly common called useIsMounted that solves this problem (for functional components)...

import { useRef, useEffect } from 'react';  export function useIsMounted() {   const isMounted = useRef(false);    useEffect(() => {     isMounted.current = true;     return () => isMounted.current = false;   }, []);    return isMounted; } 

then in your functional component

function Book() {   const isMounted = useIsMounted();   ...    useEffect(() => {     asyncOperation().then(data => {       if (isMounted.current) { setState(data); }     })   });   ... } 
vote vote

60

I had this warning possibly because of calling setState from an effect hook (This is discussed in these 3 issues linked together).

Anyway, upgrading the react version removed the warning.

Top 3 video Explaining javascript - Can't perform a React state update on an unmounted component

Related QUESTION?