Notes

React & Promises

25 May 2014

Update: Thanks to Vjeux for pointing out that React components have a method to check it they’re mounted - isMounted which I managed to miss entirely when I looked for it.

This is a short post on using Facebook’s React with Promises. I’m in the process of creating a simple App using React which makes use of Promises for long running operations, what I’ve found is that if I get bored while they’re running and navigate away to a different area of the App when the promise gets fulfilled I get errors in React as it’s trying to update state on a now unmounted component.

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

Promises don’t have an unsubscribe method so you can’t keep a reference to a promise and unsubscribe to it when the component’s mounted. So the approach I’ve taken is to have a flag to indicate whether or not a component is mounted.

It could be argued that a better solution would be to move from using Promises to using Reactive Extensions (This is entirely unrelated to React) where one can unsubscribe. Also that would provide a mechanism for propogating progress information too - but it’s not a change I’m wanting to make at this stage - so on to the band-aids.

Since I had a bunch of places I needed to do the same thing it seemed like making a mixin for it would be the way forward.

Here’s the code:

var ComponentIsMountedMixin = {
	componentWillMount: function() {
		this.componentIsMounted = false;
  	},
	componentDidMount: function() {
		this.componentIsMounted = true;
  	},
	componentWillUnmount: function() {
		this.componentIsMounted = false;
	}, 
	safeSetState: function(newState) {
		if (this.componentIsMounted) {
			this.setState(newState);
		}
	}
};

The mixin is then used in the component:

... = React.createClass({
	mixins: [ComponentIsMountedMixin],
...

The mixin’s version of the lifecycle methods get called before the component’s so the flag is ready for use in the component making use of them.

I’ve created a gist which shows how this works, I’ve used a timeout as a proxy for a call that returns a promise.