Can't Perform A React State Update On An Unmounted Component
Answer :
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 this flag denote mount status someAsyncOperation().then(data => { if (isMounted) setState(data); }) return () => { isMounted = false }; // use effect cleanup to set flag false, if unmounted });
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; // note this mounted flag fetchData(); return () => { isMounted = false; }; // use effect cleanup to set flag false, if unmounted // simulate some Web API fetching function fetchData() { setTimeout(() => { // drop "if (isMounted)" to trigger error again if (isMounted) setState("data fetched"); }, 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 just knows, how to deal with and automatically abort async functions in case the component unmounts before:
function useAsync(asyncFn, onSuccess) { useEffect(() => { let isMounted = true; asyncFn().then(data => { if (isMounted) onSuccess(data); }); return () => { isMounted = false }; }, [asyncFn, onSuccess]); }
// use async operation with automatic abortion on unmount function useAsync(asyncFn, onSuccess) { useEffect(() => { let isMounted = true; asyncFn().then(data => { if (isMounted) onSuccess(data); }); return () => { isMounted = false; }; }, [asyncFn, onSuccess]); } const Child = () => { const [state, setState] = useState("loading (4 sec)..."); useAsync(delay, 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 delay = () => 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>
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() { ... } }
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; }; }
Comments
Post a Comment