22

I'm trying to fetch the data from the server after component has been updated but I couldn't manage to do that. As far as I understand componentWillUnmount is called when component is about to be destroyed, but I never need to destroy it so it's useless to me. What would be solution for this? When I should set the state?

async componentDidUpdate(prevProps, prevState) {
  if (this.props.subject.length && prevProps.subject !== this.props.subject) {
    let result = await this.getGrades({
      student: this.props.id,
      subject: this.props.subject
    });
    this.setState({
      subject: this.props.subject,
      grades: result
    });
  }
}

async getGrades(params) {
  let response, body;

  if (params['subject'].length) {
    response = await fetch(apiRequestString.gradesBySubject(params));
    body = await response.json();
  } else {
    response = await fetch(apiRequestString.grades(params));
    body = await response.json();
  }

  if (response.status !== 200) throw Error(body.message);

  return body;
}

Full error:

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.
Nikola Stojaković
  • 2,257
  • 4
  • 27
  • 49
  • I would place a breakpoint (if using Chrome dev tools and sourcemaps) and see when does setState get called and whether the component is unmounted before `getGrades` resolves into `result`. For debugging [this](https://stackoverflow.com/a/39767963/4625144) might help when exploring the component in question. – Dimitar Nikovski May 19 '18 at 19:50
  • Clearly, React is destroying your component, so »my component never gets destroyed« doesn't appear to be a valid assumption. – Ingo Bürk May 19 '18 at 21:44
  • I wasn't aware of that. When it happens? – Nikola Stojaković May 20 '18 at 09:58
  • This is most likely a false warning - which is why the React team will remove the warning in the next release. See [PR](https://github.com/facebook/react/pull/22114) – Katie Dec 10 '21 at 06:14

2 Answers2

46

A common pattern I use in this instance is something along the lines of

componentWillUnmount() {
    this.isCancelled = true;
}

And then in the code where you're awaiting an async function to resolve, you would add a check before setting state:

async componentDidUpdate(prevProps, prevState) {
    if (this.props.subject.length && prevProps.subject !== this.props.subject) {
        let result = await this.getGrades({
            student: this.props.id,
            subject: this.props.subject
        });
        !this.isCancelled && this.setState({
            subject: this.props.subject,
            grades: result
        });
    }
}

That will stop any state setting on unmounted/unmounting components

Steve Vaughan
  • 2,163
  • 13
  • 18
1

The accepted answer works, and is a valid workaround for the problem of calling asynchronous functions in the component rendering methods (getInitialState, componentWillMount, componentDidMount).

But a better practice would be to use state management helpers like Redux and Flux and a global store, this might avoid the problem of multiple setStates.

Eliâ Melfior
  • 359
  • 4
  • 19
  • 4
    Thanks for the suggestion. Few people already recommended me to use Redux but I'm still in the process of learning and, as I haven't read about Redux, I'm still not sure when I should use it or not. I will definitely check it out after I learn the basics. – Nikola Stojaković Aug 02 '18 at 14:40
  • I'm in a similar place, my suggestion comes from actually seeing the value in Redux while having a component without it using setState too many times. And it is a little bit weird to learn, i haven't applied it in practice myself, but it is indeed valuable. A small webapp with react might not need redux, if state is managed properly, it depends on the size of the component/webapp. – Eliâ Melfior Aug 02 '18 at 18:29
  • 4
    I don't think I completely agree with this assessment. The creator of `Redux` says that use `Redux` app state management when you really want to manage a piece of information that has impacts on your entire application. For everything else use regular async calls (i.e. fetch, axios etc.), regardless of the app's size. In short, use things that are a little less awkward. In my situation the data from the async call was not redux store worthy and wiring redux for this would've been an overkill, so the original answer's solution worked best – Anjan Biswas Aug 07 '18 at 01:19
  • This implies that React throws the error because it expects projects to contain a data store. I'm not so sure that's true. – Womble Oct 24 '18 at 00:46