can't perform a react state update on an unmounted component

  • Last Update :
  • Techknowledgy :

Warning: Can't perform a React state update on an unmounted component. 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: 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 : -

This is our Splunktool team suggestion ✌, we tried and its working fine
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
2._
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
3._
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"));

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]);
}
6._
// 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"));

Suggestion : 2

To solve the "Warning: Can't perform a React state update on an unmounted component", declare an isMounted boolean in your useEffect hook that is used to track whether the component is mounted. A component's state should only be updated if the component is mounted. A straight forward way to get rid of the warning is to keep track of whether the component is mounted using an isMounted boolean in our useEffect hook. The function we returned from the useEffect hook is called when the component unmounts.

1._
Copied!import {useState, useEffect} from 'react';

const App = () => {
  const [state, setState] = useState('');

  useEffect(() => {
    // 👇️ set isMounted to true
    let isMounted = true;

    async function fetchData() {
      const result = await Promise.resolve(['hello', 'world']);

      // 👇️ only update state if component is mounted
      if (isMounted) {
        setState(result);
      }
    }

    fetchData();

    return () => {
      // 👇️ when component unmounts, set isMounted to false
      isMounted = false;
    };
  }, []);

  return (
    <div>
      <h2>State: {JSON.stringify(state)}</h2>
    </div>
  );
};

export default App;
2._
Copied!async function fetchData() {
   const result = await Promise.resolve(['hello', 'world']);

   // 👇️ only update state if component is mounted
   if (isMounted) {
      setState(result);
   }
}
3._
Copied!
   return () => {
      // 👇️ when component unmounts, set isMounted to false
      isMounted = false;
   };

Suggestion : 3

This’s warning message: 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. I’m still getting this warning after updating to version 8.1.4. It appears #125 is supposed to fix this, but I still get the warning. Any suggestions? It only happens when I navigate to the route directly, rather than being routed to it from the homepage. I solved it by rendering the component conditionally. I added a condition so that only if the object to which the component is used for (in my case a song) is returned from the server and is not empty the component will be rendered. Not sure it’s the best practice but it works.

This’s warning message:

I’m using React v16.4.0

const FanpageFacebook = () => (
  <>
    <BoxTitle title="Fanpage" showViewMoreButtom={false} />
    <div className="bg-white flex p-2.5 mb-2px">
      <FacebookProvider
        appId="469469066915707
"
      >
        <Page
          href="https://www.facebook.com/khotailieumienphi.tk"
          tabs="timeline"
        />
      </FacebookProvider>
    </div>
  </>
);

Here’s my button:

const FacebookButton = ({ appId, version, scope, onSuccess, children }) => {  
  return (
    <FacebookProvider appId={appId} version={version}>
      <Login
        scope={scope}
        onCompleted={handleResponse}
        onSuccess={() => onSuccess(result)}
        onError={handleError}
      >
        {({ loading, handleClick, error, data }) => (
          <div onClick={handleClick}>
            {children ? children : <DefaultButton loading={loading} />}
          </div>
        )}
      </Login>
    </FacebookProvider>
  );
};

Here it is used on my login page:

class LoginPage extends Component {
  onSuccess = data => {
    this.props.actions.User.login(data);
  };

  render() {
    if (this.props.isLoggedIn) return <Redirect to="/dashboard" />;
    return (
      <div>
        <h1>Login Page</h1>
        <FacebookButton {...btnConfig} onSuccess={this.onSuccess} />
      </div>
    );
  }
}

Suggestion : 4

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. React state update on an unmounted component Ok first part of our problem is accomplished, thats the React state update , now we need to create the 2nd part - An unmounted component .

1._
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>
  );
}
2._
const initialState = {
   loading: false,
   selectedPet: "",
   petData: null
}

function petsReducer(state, action) {
   switch (action.type) {
      case "PET_SELECTED": {
         return {
            ...state,
            selectedPet: action.payload
         };
      }
      case "FETCH_PET": {
         return {
            ...state,
            loading: true,
            petData: null
         };
      }
      case "FETCH_PET_SUCCESS": {
         return {
            ...state,
            loading: false,
            petData: action.payload
         };
      }

      case "RESET": {
         return initialState;
      }

      default:
         throw new Error(`Not supported action ${action.type}`);
   }
}
3._
const petsDB = {
   dogs: {
      name: "Dogs",
      voice: "Woof!",
      avatar: "🐶"
   },
   cats: {
      name: "Cats",
      voice: "Miauuu",
      avatar: "🐱"
   }
};

export function getPet(type) {
   return new Promise(resolve => {
      // simulate a fetch call
      setTimeout(() => {
         resolve(petsDB[type]);
      }, 1000);
   });
}
5._
function App() {
  const [showPets, setShowPets] = useState(true);  const toggle = () => {    setShowPets(state => !state);  };
  return (
    <div>
      <button onClick={toggle}>{showPets ? "hide" : "show"}</button>      {showPets && <Pets />}    </div>
  );
}
6._
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>
  );
}

Suggestion : 5

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. 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. With Code Examples We were able to figure out how to solve the 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. code by looking at a range of other samples.

In this session, we will try our hand at solving the 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. puzzle by using the computer language. The following piece of code will demonstrate this point.

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
   }, []);

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.. There isn’t just one way to solve a problem; rather, there are a number of distinct strategies that can be utilised. In the following examples, we will discuss a variety of different approaches that could be taken.

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

Suggestion : 6

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. In the above case, React tries to set the state of an unmounted component, which is not necessary since the component is not in scope anymore. Hence, React warns us that there is a piece of code that tries to update the state of an unmounted component. As React suggests, this will not introduce any bugs in the application, however, it might use up unnecessary memory. The component which made the call gets unmounted due to some user action (eg: user navigating away).

1._
import { useEffect, useState } from "react"

const FetchPosts = () => {
  const [posts, setPosts] = useState([])
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/posts"
        )
        console.log("received response")
        const data = await response.json()
        setPosts(data)
      } catch (e) {
        console.log(e)
      }
    }

    fetchData()
  }, [])
  return (
    <ul>
      {posts.map(post => {
        return <li key={post.id}>{post.title}</li>
      })}
    </ul>
  )
}

export default FetchPosts
2._
import React, { useState } from "react"
import FetchPosts from "./FetchPosts"

function App() {
  const [showPosts, setShowPosts] = useState()

  return (
    <div>
      <button onClick={() => setShowPosts(true)}>Fetch Posts</button>
      <button onClick={() => setShowPosts(false)}>Hide Posts</button>
      {showPosts && <FetchPosts />}
    </div>
  )
}

export default App
3._
import { useEffect, useState } from "react"

const FetchPosts = () => {
  const [posts, setPosts] = useState([])
  useEffect(() => {
    const controller = new AbortController()
    const signal = controller.signal
    const fetchData = async () => {
      try {
        const response = await fetch(
          "https://jsonplaceholder.typicode.com/posts",
          {
            signal: signal,
          }
        )
        console.log("received response")
        const data = await response.json()
        setPosts(data)
      } catch (e) {
        console.log(e)
      }
    }

    fetchData()

    return () => {
      controller.abort()
    }
  }, [])
  return (
    <ul>
      {posts.map(post => {
        return <li key={post.id}>{post.title}</li>
      })}
    </ul>
  )
}

export default FetchPosts
5._
import axios from "axios"
import { useEffect, useState } from "react"

export const AxiosPosts = () => {
  const [posts, setPosts] = useState([])
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get(
          "https://jsonplaceholder.typicode.com/posts"
        )
        console.log("received response")
        const data = response.data
        setPosts(data)
      } catch (e) {
        console.log(e)
      }
    }

    fetchData()
  }, [])
  return (
    <ul>
      {posts.map(post => {
        return <li key={post.id}>{post.title}</li>
      })}
    </ul>
  )
}

export default AxiosPosts
6._
import axios from "axios"
import { useEffect, useState } from "react"

export const AxiosPosts = () => {
  const [posts, setPosts] = useState([])
  useEffect(() => {
    let cancelToken

    const fetchData = async () => {
      cancelToken = axios.CancelToken.source()
      try {
        const response = await axios.get(
          "https://jsonplaceholder.typicode.com/posts",
          { cancelToken: cancelToken.token }
        )
        console.log("received response")
        const data = response.data
        setPosts(data)
      } catch (e) {
        console.log(e)
      }
    }

    fetchData()

    return () => {
      cancelToken.cancel("Operation canceled.")
    }
  }, [])
  return (
    <ul>
      {posts.map(post => {
        return <li key={post.id}>{post.title}</li>
      })}
    </ul>
  )
}

export default AxiosPosts

Suggestion : 7

what can I do I get the same problem that is can't perform a react state on 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 the componentWillUnmount method. in SnackbarProvider (at Dashboard.test.jsx:41) in JssProvider (created by WrapperComponent) in WrapperComponent That causes a memory leak as React can't notice some elements must be removed or created as the key changes on any other change in the collection element. My conclusion is that key should be unique during the life cycle so the state mechanism can rely on it.

1._
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
2._
class Page extends Component {
  _isMounted = false;

  state = {
    isLoading: true
  }

  componentDidMount() {
    this._isMounted = true;
  
    callAPI_or_DB(...).then(result => {
      if (this._isMounted) {
        this.setState({isLoading: false})
      }
    });
  }

  componentWillUnmount() {
    this._isMounted = false;
  }

  render() {
    return (
      <div>Whatever</div>
    );
  }
}

export default Page;
3._
     <JssProvider generateClassName={generateClassName}>
        <SnackbarProvider>
          <StaticRouter location="/" context={{}}>
            <Provider store={mStore}>
              <Route path="/" component={Dashboard} exact />
            </Provider>
          </StaticRouter>
        </SnackbarProvider>
      </JssProvider>,
5._
const MyComp = () => {
   const isMounted = useIsMounted();

   useEffect(() => {
      if (isMounted.current) {
         // update your state here
      }
   }, [isMounted]);

   /// ...
};
6._
<ul>
{collection.map(o => <li key={JSON.stringify(o)}> {o.title} </li>)}
</ul>


Suggestion : 8

What does the error: “Can’t perform a react state update on an unmounted component” signify? To fix the error (or warning), we must check, before every state update, whether the component is mounted. If the component is mounted, we will move ahead and update the state; if not, we won’t update the state. To do this, we can follow two methods: How to fix the error: “Can’t perform a react state update on an unmounted component”?Fix-1: Moving the state to a higher component in the hierarchyFix-2: Check if React has unmounted the component using the useRef React hookFix-3: Create a custom React hook to keep a check if React has unmounted the component

If a component has some state and is modified even after React has unmounted the component, we will get the warning for sure. But, if we move the state and its logic to a higher component in the hierarchy, then updating the state even after the component unmounts won’t give any warning because the state is not local to the component that was unmounted.

Therefore, to solve the error (or warning), we can move the state to a higher component in the hierarchy, and then pass it on to the child component like this:

.wp-block-code {
	border: 0;
	padding: 0;
}

.wp-block-code > div {
	overflow: auto;
}

.shcb-language {
	border: 0;
	clip: rect(1px, 1px, 1px, 1px);
	-webkit-clip-path: inset(50%);
	clip-path: inset(50%);
	height: 1px;
	margin: -1px;
	overflow: hidden;
	padding: 0;
	position: absolute;
	width: 1px;
	word-wrap: normal;
	word-break: normal;
}

.hljs {
	box-sizing: border-box;
}

.hljs.shcb-code-table {
	display: table;
	width: 100%;
}

.hljs.shcb-code-table > .shcb-loc {
	color: inherit;
	display: table-row;
	width: 100%;
}

.hljs.shcb-code-table .shcb-loc > span {
	display: table-cell;
}

.wp-block-code code.hljs:not(.shcb-wrap-lines) {
	white-space: pre;
}

.wp-block-code code.hljs.shcb-wrap-lines {
	white-space: pre-wrap;
}

.hljs.shcb-line-numbers {
	border-spacing: 0;
	counter-reset: line;
}

.hljs.shcb-line-numbers > .shcb-loc {
	counter-increment: line;
}

.hljs.shcb-line-numbers .shcb-loc > span {
	padding-left: 0.75em;
}

.hljs.shcb-line-numbers .shcb-loc::before {
	border-right: 1px solid #ddd;
	content: counter(line);
	display: table-cell;
	padding: 0 0.75em;
	text-align: right;
	-webkit-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;
	white-space: nowrap;
	width: 1%;
}
function App() {
  const [state, setState] = useState();

  return (
    <div>
      <h1>How to fix React state update error</h1>
      <Child state={state} setState={setState} />
    </div>
  );
}

function Child(props) {
  const { state, setState } = props;

  return (
    ...
  )
}Code language: JavaScript (javascript)

To solve the error (or warning), create a mutable object isComponentMounted using the useRef() hook. The isComponentMounted.current is mutable and its value is maintained during the component’s lifecycle. Now using the useEffect() lifecycle hook, we set the value of isComponentMounted.current to true. Then we create a cleanup function that runs when the component unmounts. The cleanup function will set the value of isComponentMounted.current to false. Also, we keep an empty dependency array as we want it to run on the initial render and unmount only and not on updates.

Let’s write the code to understand the process better.

const isComponentMounted = useRef();

useEffect(function() {
   isComponentMounted.current = true;
   return function() {
      isComponentMounted.current = false;
   };
}, []);
Code language: JavaScript(javascript)

Now, every time you try to update the state, make sure to check if the component is mounted or not using the isComponentMounted.current object.

if (isComponentMounted.current) {
   setState(...);
}
Code language: JavaScript(javascript)

Our hook doesn’t take any arguments and returns a boolean value representing whether React has unmounted the component. The logic we wrote inside the custom hook is the same logic we applied in the second fix to remove the error (or warning).

We may import our custom React hook, and use it inside any component as follows.

const isComponentMounted = useIsComponentMounted();
Code language: JavaScript(javascript)

Suggestion : 9

This means that although we have avoided an unnecessary setState, the memory still hasn’t cleared up. There is still an asynchronous action happening which doesn’t know that the component life cycle has ended and it is not needed anymore. 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. P.S: You can use the same signal and pass it as many XHR requests in your component as you like. When the component gets un mounted, all those XHR requests that are in a pending state will get cancelled when componentWillUnmount is called.

In my Index.js, I call Counter.js and simply do this in my render:

{showCounter ? <Counter /> : null}

Before I talk about that, I want to talk about a deprecated method in React called isMounted()

Before December 2015, there was a method called isMounted in React. You can read more about it in the React blog. What it did was something like this:

import React from 'react'
import ReactDOM from 'react-dom'
import axios from 'axios'

class RandomUser extends React.Component {
  state = {user: null}
  _isMounted = false
  handleButtonClick = async () => {
    const response = await axios.get('https://randomuser.me/api/')
    if (this._isMounted) {
      this.setState({ user: response.data })
    }
  }
  componentDidMount() {
    this._isMounted = true
  }
  componentWillUnmount() {
    this._isMounted = false
  }
  render() {
    return (
      <div>
        <button onClick={this.handleButtonClick}>Click Me</button>
        <pre>{JSON.stringify(this.state.user, null, 2)}</pre>
      </div>
    )
  }
}

Let’s look a bit more in depth here. With code, of course, because everyone ❤ code.

var myController = new AbortController();
var mySignal = myController.signal;

var downloadBtn = document.querySelector('.download');
var abortBtn = document.querySelector('.abort');

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', function() {
   myController.abort();
   console.log('Download aborted');
});

function fetchVideo() {
   ...
   fetch(url, {
      signal: mySignal
   }).then(function(response) {
      ...
   }).catch(function(e) {
      reports.textContent = 'Download error: ' + e.message;
   })
}

Let’s carry on just a little bit more. Normally, your XHR requests are in one file, and your main container component is in another (from which you call that API method). How do you pass that signal to another file and still get that XHR request cancelled?

Here is how you do it:

import React, { Component } from 'react';
import axios from 'axios';

// API
import { onLoadUser } from './UserAPI';

class Example extends Component {
  signal = axios.CancelToken.source();

  state = {
    isLoading: false,
    user: {},
  }
  
  componentDidMount() {
    this.onLoadUser();
  }
  
  componentWillUnmount() {
    this.signal.cancel('Api is being canceled');
  }
  
  onLoadUser = async () => {
    try {
      this.setState({ isLoading: true });
      const data = await onLoadUser(this.signal.token);
      this.setState({ user: data, isLoading: true });
    } catch (error) {
      if (axios.isCancel(err)) {
        console.log('Error: ', err.message); // => prints: Api is being canceled
      } else {
        this.setState({ isLoading: false });
      }
    }
  }
    
    render() {
      return (
        <div>
          <pre>{JSON.stringify(this.state.user, null, 2)}</pre>
        </div>
      )
    }
  };
 
}

Here is how you do it:

export const onLoadUser = async myCancelToken => {
   try {
      const {
         data
      } = await axios.get('https://randomuser.me/api/', {
         cancelToken: myCancelToken,
      })
      return data;
   } catch (error) {
      throw error;
   }
};

Suggestion : 10

I am writing an application in React and was unable to avoid a super common pitfall, which is calling setState(...) after componentWillUnmount(...). Doing a bit more research on this, it appears that some internal Google reasons prevented cancelable promises from coming into the standard. So after clicking submit the modal was automatically hidden becasue of the parent condition (someCondition). 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:

This is our Splunktool team suggestion ✌, we tried and its working fine
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

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
3._
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"));

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]);
}
6._
// 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"));