2
  componentDidMount () {
        this.showPosts();
  }

  showPosts = async () => {

    var userID = await AsyncStorage.getItem('userID');

    fetch(strings.baseUri+"getPostWithUserID", {
        method: 'POST',
        headers: {
           Accept: 'application/json',
           'Content-Type': 'application/json'
        },
        body: JSON.stringify({ 
            "user_id": userID
        })
        })
        .then((response) => response.json())
        .then((responseJson) => {

          this.setState({show: false}); // If I comment this line, then I don't get the warning.

        })
        .catch((error) => {
            console.error(error);
        });
  }

How to use componentWillUnmount because I'm getting the warning below. Is there a way I can setState show to true when I use componentWillUnmount? Warning

Shubham Bisht
  • 577
  • 2
  • 26
  • 51
  • 1
    Possible duplicate of [How to cancel a fetch on componentWillUnmount](https://stackoverflow.com/questions/49906437/how-to-cancel-a-fetch-on-componentwillunmount) – Andrew Jan 21 '19 at 11:45
  • @Andrew is it possible if you could give an answer in my scenario? I'm not able to understand on how to use what's given in the link you provided. TIA :) – Shubham Bisht Jan 21 '19 at 12:30

4 Answers4

3

You are mixing a couple of things in your code. You are using await but not calling await when you use this.showPosts(). You are also not wrapping your await in a try/catch as await can throw.

There a couple of ways that you can solve the problem of setting state on an unmounted component. The most simplest, though it is an anti-pattern is to do is to set a variable in the componentDidMount and the componentWillUnmount that tracks the mounted state of the component.

So let's refactor your code so that it makes more sense

This is how your componentDidMount and the componentWillUnmount will now look.

async componentDidMount () {
  this._isMounted = true;
  await this.showPosts();
}

componentWillUnmount () {
  this._isMounted = false;
}

Updating showPosts so that it is purely async/await

showPosts = async () => {
  try {
    var userID = await AsyncStorage.getItem('userID');
    let response = await fetch(strings.baseUri + 'getPostWithUserID', {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        'user_id': userID
      })
    });

    let responseJson = await response.json();
    if (this._isMounted) {
      this.setState({show: false});
    }
  } catch (err) {
    console.error(error);
  }
}

Or if we use your current implementation of showPosts it would look like this, but fixing the lack of try/catch around the await.

showPosts = async () => {
  try {
    var userID = await AsyncStorage.getItem('userID');

    fetch(strings.baseUri + 'getPostWithUserID', {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        'user_id': userID
      })
    })
      .then((response) => response.json())
      .then((responseJson) => {
        if (this._isMounted) {
          this.setState({show: false}); // If I comment this line, then I don't get the warning.
        }
      })
      .catch((error) => {
        console.error(error);
      });
  } catch (err) {
    console.warn(err);
  }
}

An alternative is to get into cancelling promises once they have been made. This article goes someway to explaining how to do it https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html

Andrew
  • 26,706
  • 9
  • 85
  • 101
  • THANK YOU SO MUCH @Andrew. It finally worked for me. But I've a question, why did we make the variable using `this._isMounted`. I mean can you please tell me where it is written that you make variable this way. – Shubham Bisht Jan 21 '19 at 13:22
  • Also I just checked that **isMounted before calling setState() does eliminate the warning**. So the solution you gave me, is it the right way or a hack? – Shubham Bisht Jan 21 '19 at 13:29
  • `this.isMounted()` is deprecated and will be removed at some time in the future. Best not to use deprecated functions. – Andrew Jan 21 '19 at 13:31
  • no I mean the overall solution is it the right way? Because the article said that `if (this.isMounted()) { this.setState({ }) }` this eliminates the warning – Shubham Bisht Jan 21 '19 at 13:35
  • Yes it is a valid solution. The article says: *An easy migration strategy for anyone upgrading their code to avoid isMounted() is to track the mounted status yourself. Just set a _isMounted property to true in componentDidMount and set it to false in componentWillUnmount, and use this variable to check your component’s status.* So what we have done is avoided using a deprecated API and used a solution that works. – Andrew Jan 21 '19 at 13:42
  • @ShubhamBisht I don't use `react-native-video` – Andrew Jan 30 '19 at 09:33
0

You can check if component is mounted this way, and then check this variable inside your async function to see if you can still running the function or otherwise to cancel it:

componentDidMount() { 
  this.mounted = true;
}

componentWillUnmount() {
  this.mounted = false;
}

async asyncFunction {
  if(this.isMounted){
    setState(....);
  }else{
    return;
  }
}
SmoggeR_js
  • 2,950
  • 4
  • 22
  • 52
0

you can use some internal component object property as - isComponentMounted and then check on it during async operation callback.

Kasia
  • 1,665
  • 1
  • 10
  • 16
  • How to do that? – Shubham Bisht Jan 21 '19 at 11:52
  • you would need to define it by yourself and then control it with lifecycle methods - componentDidMount () { this.isComponentMounted = true }, componentWillUnmount () { this.isComponentMounted = false } – Kasia Jan 22 '19 at 08:48
0

Not a good idea to set state in componentDidMount without an initial state in the constructor because it triggers an extra render, which can cause performance issues.

From the official documentation:

You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state. Use this pattern with caution because it often causes performance issues. In most cases, you should be able to assign the initial state in the constructor() instead. It can, however, be necessary for cases like modals and tooltips when you need to measure a DOM node before rendering something that depends on its size or position.

https://reactjs.org/docs/react-component.html#componentdidmount

But this gets back to WHY you would want to use componentWillUnmount? That is for unregistering events, push notifications, cleaning up resources.

Charles Owen
  • 2,403
  • 1
  • 14
  • 25