Warning: Can't perform a React state update on an unmounted component after update context and redirect to another page

Asked
Active3 hr before
Viewed126 times

8 Answers

contextstateunmountedupdateperformcomponentwarningreact
90%

Warning: Can't perform a React state update on an unmounted component.,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 : -, Possible duplicate of React - setState() on unmounted component – Emile Bergeron Aug 27 '19 at 19:23

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>

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>
load more v
88%

React state update on an unmounted component,An unmounted component,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.

function Pets() {
  const [pets, dispatch] = useReducer(petsReducer, initialState);

  const onChange = ({ target }) => {
    dispatch({ type: "PET_SELECTED", payload: target.value });
  };

  useEffect(() => {
    if (pets.selectedPet) {
      dispatch({ type: "FETCH_PET" });
      getPet(pets.selectedPet).then(data => {
        dispatch({ type: "FETCH_PET_SUCCESS", payload: data });
      });
    } else {
      dispatch({ type: "RESET" });
    }
  }, [pets.selectedPet]);

  return (
    <div>
      <select value={pets.selectedPet} onChange={onChange}>
        <option value="">Select a pet</option>
        <option value="cats">Cats</option>
        <option value="dogs">Dogs</option>
      </select>
      {pets.loading && <div>Loading...</div>}
      {pets.petData && <Pet {...pets.petData} />}
    </div>
  );
}
load more v
72%

But if you invoke setState within the context of an asynchronous operation, then you might run into the React warning “Can’t…,React setState is used to update the state of our components. Normally setState is called during the lifetime of your component. In the happy path you won’t have any issues — setState will execute, and your state will successfully update.

Pretag
 Pretag team - issue, fix, solve, resolve
65%

look at it: https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component,@5ervant you can use useRef for check if the component still mounted,For functional components you can use a custom hook:

index.js: 1452 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 the componentWillUnmount method. in RippledComponent in div in ChipSet
load more v
75%

Pretag
 Pretag team - issue, fix, solve, resolve
40%

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.,Warning: Can't call setState (or forceUpdate) 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 the componentWillUnmount method.,The shown warning(s) usually show up when this.setState() is called in a component even though the component got already unmounted. The unmounting can happen for different cases:

But what about asynchronous requests in React components? It can happen that you , which will call this.setState() eventually to set the result in the local state of the component. But what if the component has unmounted before. Then the warning will show up, because React cannot set the result in state when the component isn't there anymore.

class News extends Component {  constructor(props) {    super(props);     this.state = {      news: [],    };  }   componentDidMount() {    axios      .get('https://hn.algolia.com/api/v1/search?query=react')      .then(result =>        this.setState({          news: result.data.hits,        }),      );  }   render() {    return (      <ul>        {this.state.news.map(topic => (          <li key={topic.objectID}>{topic.title}</li>        ))}      </ul>    );  }}
class News extends Component {  constructor(props) {    super(props);     this.state = {      news: [],    };  }   componentDidMount() {    axios      .get('https://hn.algolia.com/api/v1/search?query=react')      .then(result =>        this.setState({          news: result.data.hits,        }),      );  }   render() {    return (      <ul>        {this.state.news.map(topic => (          <li key={topic.objectID}>{topic.title}</li>        ))}      </ul>    );  }}

You can handle this issue either by aborting the request when your component unmounts or preventing this.setState() on an unmounted component. However, most promise-based libraries/API don't implement aborting a request, so we add a workaround, introducing a class field that holds the lifecycle state of your component, to prevent this.setState() being called. It can be initialized as false when the component initializes, changed to true when the component mounted, and then reset to false when the component unmounted. This way, you can keep track of your component's lifecycle state. It doesn't affect the local state stored and modified with this.state and this.setState(), because you can access it directly on the component instance without relying on React's local state management. Moreover, it doesn't lead to any re-rendering of the component when the class field is changed.

class News extends Component {
   _isMounted = false;
   constructor(props) {
      super(props);
      this.state = {
         news: [],
      };
   }
   componentDidMount() {
      this._isMounted = true;
      axios.get('https://hn.algolia.com/api/v1/search?query=react').then(result => this.setState({
         news: result.data.hits,
      }), );
   }
   componentWillUnmount() {
      this._isMounted = false;
   }
   render() {
      ...
   }
}
class News extends Component {
   _isMounted = false;
   constructor(props) {
      super(props);
      this.state = {
         news: [],
      };
   }
   componentDidMount() {
      this._isMounted = true;
      axios.get('https://hn.algolia.com/api/v1/search?query=react').then(result => this.setState({
         news: result.data.hits,
      }), );
   }
   componentWillUnmount() {
      this._isMounted = false;
   }
   render() {
      ...
   }
}

Finally, you can use this knowledge not to abort the request itself, but avoid calling this.setState() on your component instance, even though the component already unmounted. It will prevent the warning.

class News extends Component {
   _isMounted = false;
   constructor(props) {
      super(props);
      this.state = {
         news: [],
      };
   }
   componentDidMount() {
      this._isMounted = true;
      axios.get('https://hn.algolia.com/api/v1/search?query=react').then(result => {
         if (this._isMounted) {
            this.setState({
               news: result.data.hits,
            });
         }
      });
   }
   componentWillUnmount() {
      this._isMounted = false;
   }
   render() {
      ...
   }
}
class News extends Component {
   _isMounted = false;
   constructor(props) {
      super(props);
      this.state = {
         news: [],
      };
   }
   componentDidMount() {
      this._isMounted = true;
      axios.get('https://hn.algolia.com/api/v1/search?query=react').then(result => {
         if (this._isMounted) {
            this.setState({
               news: result.data.hits,
            });
         }
      });
   }
   componentWillUnmount() {
      this._isMounted = false;
   }
   render() {
      ...
   }
}
load more v
22%

Now we know explicitly that the API was aborted, because the component was unmounted and therefore logs an error. But we know that we no longer need to update that state since it is no longer required.,My goal for this article is to make sure that no one ever has to face this error and not know what to do about it again.,I’ll talk about subscriptions first, and then we’ll move on to handling asynchronous task cancellation to avoid memory leaks in React (the main purpose of this article). If not handled, this slows our app down.

Pretag
 Pretag team - issue, fix, solve, resolve
60%

When you update a state from an unmounted component, React will throw 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 the componentWillUnmount method.",The clean up function is the equivalent of componentWillUnmount in a React Class component.

useEffect(() => {
   //Do all the job, for example, subscribe a websocket channel
   return function() {
      //Unsubscribe websocket channel
   };
}, []);
load more v

Other "context-state" queries related to "Warning: Can't perform a React state update on an unmounted component after update context and redirect to another page"