问题
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.
回答1:
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
回答2:
You can use component.isMounted
method to check if component was actually attached to the DOM before replacing its state. Docs.
isMounted()
returnstrue
if the component is rendered into the DOM,false
otherwise. You can use this method to guard asynchronous calls tosetState()
orforceUpdate()
.
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.
回答3:
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.
来源:https://stackoverflow.com/questions/25067364/set-reactjs-state-asynchronously