Question

If you do an asynchronous action that updates the state in componentWillMount (like the docs say), but the component is unmounted (the user navigates away) before that async call is complete, you end up with the async callback trying to set the state on a now unmounted component, and an

"Invariant Violation: replaceState(...): Can only update a mounted or mounting component."

error.

What's the best way around this?

Thanks.

Était-ce utile?

La solution

update 2016

Don't start using isMounted because it will be removed from React, see the docs.

Probably the best solution for problems arising from async call from cmomponentWillMount is to move things to componentDidMount.

More info about how to properly get around this problem and how not to need to use isMounted here: isMounted is an Antipattern

Autres conseils

You can use component.isMounted method to check if component was actually attached to the DOM before replacing its state. Docs.

isMounted() returns true if the component is rendered into the DOM, false otherwise. You can use this method to guard asynchronous calls to setState() or forceUpdate().

UPD: Before you downvote. This answer was given 2 freaking years ago. And it was the way to do stuff back that days. If you are just starting to use React do not follow this answer. Use componentDidMount or whatever another lifecycle hook you need.

isMounted() is actually an easy way to solve most problems, however, I don't think this is an ideal solution for concurrency issues.

Now imagine that the user clicks very fastly on many buttons, or maybe he has a very poor mobile connection. It may happen that finally 2 concurrent requests are pending, and on completion will update the state.

If you fire request 1 and then request 2, then you would expect the result of request 2 to be added to your state.

Now imagine for some reason request 2 ends up before request 1, this will probably make your app unconsistent because it will show request2 results and then request 1, while your last "interest" was actually in request 1 answer.

To solve this kind of issue, you should rather use some kind of Compare And Swap algorithm. Basically, this means before issuing the request, you put some object node in state, and on request completion, you compare with reference equality if the node to swap is still be node you are interested in when the request completes.

Something like this:

var self = this;
var resultNode = {};
this.setState({result: resultNode});
this.getResult().then(function(someResult) {
    if ( self.state.result === resultNode ) {
        self.setState({result: someResult})
    }
}):

With something like that, you will not have concurrency issue in case the user clicks to fast on the buttons leading to current requests inside the same component.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top