Don't initialize reconcile transaction on server

...when calling setState from within a componentWillMount. Fixes #1866.

Test Plan: jest

(cherry picked from commit 54d91c293d)
This commit is contained in:
Ben Alpert
2014-07-18 10:58:37 -07:00
committed by Paul O’Shannessy
parent 975ce371e8
commit df0a678fbb
2 changed files with 45 additions and 3 deletions
@@ -31,12 +31,13 @@ require('mock-modules')
var mocks = require('mocks');
var ExecutionEnvironment;
var React;
var ReactMarkupChecksum;
var ReactMount;
var ReactReconcileTransaction;
var ReactTestUtils;
var ReactServerRendering;
var ReactMarkupChecksum;
var ExecutionEnvironment;
var ID_ATTRIBUTE_NAME;
@@ -44,12 +45,14 @@ describe('ReactServerRendering', function() {
beforeEach(function() {
require('mock-modules').dumpCache();
React = require('React');
ReactMarkupChecksum = require('ReactMarkupChecksum');
ReactMount = require('ReactMount');
ReactTestUtils = require('ReactTestUtils');
ReactReconcileTransaction = require('ReactReconcileTransaction');
ExecutionEnvironment = require('ExecutionEnvironment');
ExecutionEnvironment.canUseDOM = false;
ReactServerRendering = require('ReactServerRendering');
ReactMarkupChecksum = require('ReactMarkupChecksum');
var DOMProperty = require('DOMProperty');
ID_ATTRIBUTE_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
@@ -373,5 +376,25 @@ describe('ReactServerRendering', function() {
'a valid ReactComponent.'
);
});
it('allows setState in componentWillMount without using DOM', function() {
var Component = React.createClass({
componentWillMount: function() {
this.setState({text: 'hello, world'});
},
render: function() {
return <div>{this.state.text}</div>;
}
});
ReactReconcileTransaction.prototype.perform = function() {
// We shouldn't ever be calling this on the server
throw new Error('Browser reconcile transaction should not be used');
};
var markup = ReactServerRendering.renderComponentToString(
<Component />
);
expect(markup.indexOf('hello, world') >= 0).toBe(true);
});
});
});
+19
View File
@@ -172,6 +172,25 @@ var flushBatchedUpdates = ReactPerf.measure(
// componentDidUpdate) but we need to check here too in order to catch
// updates enqueued by setState callbacks.
while (dirtyComponents.length) {
var allUnmounted = true;
for (var i = 0, l = dirtyComponents.length; i < l; i++) {
if (dirtyComponents[i].isMounted()) {
allUnmounted = false;
break;
}
}
if (allUnmounted) {
// All the "dirty" components are unmounted, which probably means that
// they were marked dirty due to setState calls in componentWillMount
// handlers and the components are currently in the process of mounting.
// `runBatchedUpdates` will be a noop. In that case, initializing the
// DOM-dependent ReactReconcileTransaction is thus not what we want to
// do, especially when using server rendering, so we skip it.
dirtyComponents.length = 0;
return;
}
var transaction = ReactUpdatesFlushTransaction.getPooled();
transaction.perform(runBatchedUpdates, null, transaction);
ReactUpdatesFlushTransaction.release(transaction);