@samitouri / QOS-React-2 / commits / 54d91c293d

Don't initialize reconcile transaction on server

...when calling setState from within a componentWillMount. Fixes #1866. Test Plan: jest

Ben Alpert committed Jul 18, 2014 at 10:58 UTC 54d91c293d39ea82a84f7c9ed4e492ff6c72a811
2 files changed +45 -3
src/browser/server/__tests__/ReactServerRendering-test.js
+26 -3
@@ -31,12 +31,13 @@ require('mock-modules')
31
32 var mocks = require('mocks');
33
34 +var ExecutionEnvironment;
35 var React;
36 +var ReactMarkupChecksum;
37 var ReactMount;
38 +var ReactReconcileTransaction;
39 var ReactTestUtils;
40 var ReactServerRendering;
38 -var ReactMarkupChecksum;
39 -var ExecutionEnvironment;
41
42 var ID_ATTRIBUTE_NAME;
43
@@ -44,12 +45,14 @@ describe('ReactServerRendering', function() {
45 beforeEach(function() {
46 require('mock-modules').dumpCache();
47 React = require('React');
48 + ReactMarkupChecksum = require('ReactMarkupChecksum');
49 ReactMount = require('ReactMount');
50 ReactTestUtils = require('ReactTestUtils');
51 + ReactReconcileTransaction = require('ReactReconcileTransaction');
52 +
53 ExecutionEnvironment = require('ExecutionEnvironment');
54 ExecutionEnvironment.canUseDOM = false;
55 ReactServerRendering = require('ReactServerRendering');
52 - ReactMarkupChecksum = require('ReactMarkupChecksum');
56
57 var DOMProperty = require('DOMProperty');
58 ID_ATTRIBUTE_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
@@ -373,5 +376,25 @@ describe('ReactServerRendering', function() {
376 'a valid ReactComponent.'
377 );
378 });
379 +
380 + it('allows setState in componentWillMount without using DOM', function() {
381 + var Component = React.createClass({
382 + componentWillMount: function() {
383 + this.setState({text: 'hello, world'});
384 + },
385 + render: function() {
386 + return <div>{this.state.text}</div>;
387 + }
388 + });
389 +
390 + ReactReconcileTransaction.prototype.perform = function() {
391 + // We shouldn't ever be calling this on the server
392 + throw new Error('Browser reconcile transaction should not be used');
393 + };
394 + var markup = ReactServerRendering.renderComponentToString(
395 + <Component />
396 + );
397 + expect(markup.indexOf('hello, world') >= 0).toBe(true);
398 + });
399 });
400 });
src/core/ReactUpdates.js
+19
@@ -172,6 +172,25 @@ var flushBatchedUpdates = ReactPerf.measure(
172 // componentDidUpdate) but we need to check here too in order to catch
173 // updates enqueued by setState callbacks.
174 while (dirtyComponents.length) {
175 + var allUnmounted = true;
176 + for (var i = 0, l = dirtyComponents.length; i < l; i++) {
177 + if (dirtyComponents[i].isMounted()) {
178 + allUnmounted = false;
179 + break;
180 + }
181 + }
182 +
183 + if (allUnmounted) {
184 + // All the "dirty" components are unmounted, which probably means that
185 + // they were marked dirty due to setState calls in componentWillMount
186 + // handlers and the components are currently in the process of mounting.
187 + // `runBatchedUpdates` will be a noop. In that case, initializing the
188 + // DOM-dependent ReactReconcileTransaction is thus not what we want to
189 + // do, especially when using server rendering, so we skip it.
190 + dirtyComponents.length = 0;
191 + return;
192 + }
193 +
194 var transaction = ReactUpdatesFlushTransaction.getPooled();
195 transaction.perform(runBatchedUpdates, null, transaction);
196 ReactUpdatesFlushTransaction.release(transaction);