0
votes

I am performing an API call that kicks off with componentDidMount, however when a new component is loaded by the user, there is a notification for a potential memory leak, see below for message. I have researched different solutions but have found nothing yet that works, I'm wondering if this can be fixed within this particular component with a componentWillUnmount or if it is better handled within the axios call itself.

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.

componentDidMount() {
    this.loadBackground();
    this.getUpdatedWeather();
    this.getNewMartianPhotos();
  }

  checkMartianPhotos = () => {
    if (this.state.martianMotion) {
      console.log('still shooting');
      this.getNewMartianPhotos();
    } else {
      return console.log('done now');
    }
  };

  getNewMartianPhotos = () => {
    let loadedImage = '';
    let loadedInfo = '';
    let loadedMeta = '';
    let totalImage;

    API.getMarsPhotos().then(data => {
      // console.log(data.data);
      // console.log(
      //   data.data.collection.items[this.state.martianCount].data[0].photographer
      // );
      // console.log(
      //   data.data.collection.items[this.state.martianCount].data[0].description
      // );
      // console.log(
      //   data.data.collection.items[this.state.martianCount].links[0].href
      // );

      totalImage = data.data.collection.items.length;
      loadedImage =
        data.data.collection.items[this.state.martianCount].links[0].href;
      loadedInfo =
        data.data.collection.items[this.state.martianCount].data[0].description;
      loadedMeta =
        data.data.collection.items[this.state.martianCount].data[0]
          .photographer;

      this.setState({
        martianImage: loadedImage,
        martianDescription: loadedInfo,
        martianMeta: loadedMeta,
        martianCount: this.state.martianCount + 1
      });

      if (this.state.martianCount < totalImage) {
        console.log(
          `shooting off, image count now ${this.state.martianCount} against ${totalImage}`
        );
        setTimeout(this.checkMartianPhotos, 10000);
      }
    });
  };

  componentWillUnmount() {
    clearTimeout(this.checkMartianPhotos);
  }


-------------------------------------

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

  getMarsPhotos: () =>
    axios
      .get('https://images-api.nasa.gov/search?q=martian', {
        cancelToken: source.token
      })
      .catch(function(thrown) {
        if (axios.isCancel(thrown)) {
          console.log('request canceled', thrown.message);
        } else {
          console.log('there is an error that needs to be handled');
        }
      })
1
Try cancelling the request in componentWillUnmount.cbr
Maybe canceling the request is not enough and you need canceling the promise see reactjs.org/blog/2015/12/16/ismounted-antipattern.html. if you use redux, you can use redux-saga for cancellation. But first understand why this happens, maybe it's bad implementation. share codesandbox for better examination.gadi tzkhori

1 Answers

0
votes

As the error informs, your component is calling setState after it has been unMounted. This is because you are clearing your timeout incorrectly. You should be doing the following to properly clear your timeout on unmount.

this.id = setTimeout(this.checkMartianPhotos, 10000);

And then clear with

clearTimeout(this.id)

In componentWillUnmount

Also, try to move your async logic (api calls) out of your component.

Refer to this to see how to stop setting a state after an API call on unmounted component.