In React, you can setup components that fetch their own data and set their own state with that data. Because of the async nature of data fetching, you’ll have to make sure to keep things cleaned up to avoid the error of trying to set state in an unmounted component.
Updated for React 0.13. See below
React Components setState
If a React component fetches its own data, it will usually do so in the
componentDidMount method. In the data request callback, it will set its own state via the
this.setState method. (
this.state should be treated as immutable.) State should only be set on mounted components, or those components that are inserted into the dom.
If you attempt to set state on an unmounted component, you’ll get an error that looks like this:
Others have reported this variation:
Both indicate that you’re setting state on a component that is not mounted. How should one avoid this? In my case,
setState was being called in a callback that was firing after the component that initially started the request had already been unmounted from the dom. Here are the two ways I addressed the problem…
Assure Component isMounted
If the component is mounted,
setState is a safe bet. If it’s not mounted, never do it. If it’s not mounted, you probably don’t about the state at that point either. So wrap your state setting in
1 2 3 4 5 6 7 8 9 10 11 12
It feels a bit like a hack, but it makes the code safe. Another option…
Abort the Request
My asynchronous action is a network request that, when finished, will call the callback function. When the component unmounts, I can just throw away the request so the callback is never invoked. To do this, we’ll take advantage of another React lifecycle hook,
1 2 3 4 5 6 7 8 9 10 11
1 2 3 4 5 6 7 8 9
Also note that in my
end function callback, I’m checking for the existence of data. This is because when a request is aborted, data will come back as undefined.
My favored method for solving the problem is the latter request abort method. It feels cleaner that we’re relying on the lifecycle functions of the component to deal with cleanup, much like we would for events. How have you dealt with this problem?
Update: Using ES6 Classes
With React 0.13, you can define your components using the ES6
class definition. These classes conspicuously change behavior from the old style of
React.createClass in several ways. Here are a couple that are relevant here:
- There is no
isMounted()method any more
- There is no
getDOMNode()method any more
isMounted is gone, awol, as far as I can tell, and
getDOMNode has been moved to a utility function.
So, in order to get the DOM Node, you’d call it like this:
It functions as before.
In order to check
isMounted, you’re on your own. My basic implementation can be seen in the following jsbin. Frankly, it feels dirty.
React.findDOMNode will throw an exception if the component isn’t mounted, so we are using exceptions for flow control. Egh:
1 2 3 4 5 6 7 8 9 10
How it might still be used:
This leaves me with the question of why
isMounted was removed if it helps solve a potential problem. Do we solve it in another way? Does the core library do more for you? You can verify for yourself in the jsbin above that if you remove the
isMounted check, the old invariant error message is still logged.
Does anyone have a good