@samitouri / QOS-React / commits / ef99e7e096

Call setState callbacks enqueued in cWM (#8207)

Also fixes return value of ReactNativeMount and moves that callback to be after cDM instead of after all updates.

Ben Alpert committed Nov 5, 2016 at 14:38 UTC ef99e7e096cb661de0e72f93a442fbfeaaa07b1c
5 files changed +66 -45
src/renderers/dom/stack/client/ReactMount.js
+10 -5
@@ -357,7 +357,8 @@ var ReactMount = {
357 nextElement,
358 container,
359 shouldReuseMarkup,
360 - context
360 + context,
361 + callback
362 ) {
363 // Various parts of our code (such as ReactCompositeComponent's
364 // _renderValidatedComponent) assume that calls to render aren't nested;
@@ -380,6 +381,12 @@ var ReactMount = {
381 ReactBrowserEventEmitter.ensureScrollValueMonitoring();
382 var componentInstance = instantiateReactComponent(nextElement, false);
383
384 + if (callback) {
385 + componentInstance._pendingCallbacks = [function() {
386 + callback.call(componentInstance._renderedComponent.getPublicInstance());
387 + }];
388 + }
389 +
390 // The initial render is synchronous but any updates that happen during
391 // rendering, in componentWillMount or componentDidMount, will be batched
392 // according to the current batching strategy.
@@ -529,11 +536,9 @@ var ReactMount = {
536 nextWrappedElement,
537 container,
538 shouldReuseMarkup,
532 - nextContext
539 + nextContext,
540 + callback
541 )._renderedComponent.getPublicInstance();
534 - if (callback) {
535 - callback.call(component);
536 - }
542 return component;
543 },
544
src/renderers/native/ReactNativeMount.js
+7 -4
@@ -134,6 +134,12 @@ var ReactNativeMount = {
134 var instance = instantiateReactComponent(nextWrappedElement, false);
135 ReactNativeMount._instancesByContainerID[containerTag] = instance;
136
137 + if (callback) {
138 + instance._pendingCallbacks = [function() {
139 + callback.call(instance._renderedComponent.getPublicInstance());
140 + }];
141 + }
142 +
143 // The initial render is synchronous but any updates that happen during
144 // rendering, in componentWillMount or componentDidMount, will be batched
145 // according to the current batching strategy.
@@ -143,10 +149,7 @@ var ReactNativeMount = {
149 instance,
150 containerTag
151 );
146 - var component = instance.getPublicInstance();
147 - if (callback) {
148 - callback.call(component);
149 - }
152 + var component = instance._renderedComponent.getPublicInstance();
153 return component;
154 },
155
src/renderers/native/__tests__/ReactNativeMount-test.js
+21
@@ -58,4 +58,25 @@ describe('ReactNative', () => {
58 expect(UIManager.updateView).toBeCalledWith(3, 'View', { foo: 'bar' });
59 });
60
61 + it('should be able to create and update a native component', () => {
62 + var View = createReactNativeComponentClass({
63 + validAttributes: { foo: true },
64 + uiViewClassName: 'View',
65 + });
66 +
67 + var a;
68 + var b;
69 + var c = ReactNative.render(
70 + <View foo="foo" ref={(v) => a = v} />,
71 + 11,
72 + function() {
73 + b = this;
74 + }
75 + );
76 +
77 + expect(a).toBeTruthy();
78 + expect(a).toBe(b);
79 + expect(a).toBe(c);
80 + });
81 +
82 });
src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
+12
@@ -383,6 +383,18 @@ var ReactCompositeComponent = {
383 }
384 }
385
386 + // setState callbacks during willMount should end up here
387 + const callbacks = this._pendingCallbacks;
388 + if (callbacks) {
389 + this._pendingCallbacks = null;
390 + for (let i = 0; i < callbacks.length; i++) {
391 + transaction.getReactMountReady().enqueue(
392 + callbacks[i],
393 + inst
394 + );
395 + }
396 + }
397 +
398 return markup;
399 },
400
src/renderers/shared/stack/reconciler/__tests__/ReactCompositeComponentState-test.js
+16 -36
@@ -11,8 +11,6 @@
11
12 'use strict';
13
14 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
15 -
14 var React;
15 var ReactDOM;
16
@@ -171,40 +169,22 @@ describe('ReactCompositeComponent-state', () => {
169 ['componentDidMount-end', 'orange'],
170 ];
171
174 - if (ReactDOMFeatureFlags.useFiber) {
175 - // The setState callbacks in componentWillMount, and the initial callback
176 - // passed to ReactDOM.render, should be flushed right after component
177 - // did mount:
178 - expected.push(
179 - ['setState-sunrise', 'orange'], // 1
180 - ['setState-orange', 'orange'], // 2
181 - ['initial-callback', 'orange'], // 3
182 - ['shouldComponentUpdate-currentState', 'orange'],
183 - ['shouldComponentUpdate-nextState', 'yellow'],
184 - ['componentWillUpdate-currentState', 'orange'],
185 - ['componentWillUpdate-nextState', 'yellow'],
186 - ['render', 'yellow'],
187 - ['componentDidUpdate-currentState', 'yellow'],
188 - ['componentDidUpdate-prevState', 'orange'],
189 - ['setState-yellow', 'yellow'],
190 - );
191 - } else {
192 - // There is a bug in the stack reconciler where those callbacks are
193 - // enqueued, but aren't called until the next flush.
194 - expected.push(
195 - ['shouldComponentUpdate-currentState', 'orange'],
196 - ['shouldComponentUpdate-nextState', 'yellow'],
197 - ['componentWillUpdate-currentState', 'orange'],
198 - ['componentWillUpdate-nextState', 'yellow'],
199 - ['render', 'yellow'],
200 - ['componentDidUpdate-currentState', 'yellow'],
201 - ['componentDidUpdate-prevState', 'orange'],
202 - ['setState-sunrise', 'yellow'], // 1
203 - ['setState-orange', 'yellow'], // 2
204 - ['setState-yellow', 'yellow'],
205 - ['initial-callback', 'yellow'] // 3
206 - );
207 - }
172 + // The setState callbacks in componentWillMount, and the initial callback
173 + // passed to ReactDOM.render, should be flushed right after component
174 + // did mount:
175 + expected.push(
176 + ['setState-sunrise', 'orange'], // 1
177 + ['setState-orange', 'orange'], // 2
178 + ['initial-callback', 'orange'], // 3
179 + ['shouldComponentUpdate-currentState', 'orange'],
180 + ['shouldComponentUpdate-nextState', 'yellow'],
181 + ['componentWillUpdate-currentState', 'orange'],
182 + ['componentWillUpdate-nextState', 'yellow'],
183 + ['render', 'yellow'],
184 + ['componentDidUpdate-currentState', 'yellow'],
185 + ['componentDidUpdate-prevState', 'orange'],
186 + ['setState-yellow', 'yellow'],
187 + );
188
189 expected.push(
190 ['componentWillReceiveProps-start', 'yellow'],