@samitouri / QOS-React / commits / e43aaab254

Use _hostContainerInfo to track test renderer options (#8261)

* Use _hostContainerInfo to track test renderer options The transaction is not available when unmounting or updating the instance, so we track it using _hostContainerInfo * Throw if hostContainerInfo is not populated in getPublicInstance * Linting fixes * Remove transaction from ref lifecycle code path We don't need to pass the transaction around anymore since we store the test options on _hostContainerInfo instead * Remove unused argument

Brandon Dail committed Nov 11, 2016 at 08:35 UTC e43aaab2547870bc80c0b778604c5ee55b1d87f0
8 files changed +66 -25
scripts/fiber/tests-passing.txt
+2
@@ -1217,6 +1217,8 @@ src/renderers/testing/__tests__/ReactTestRenderer-test.js
1217 * gives a ref to native components
1218 * warns correctly for refs on SFCs
1219 * allows an optional createNodeMock function
1220 +* supports unmounting when using refs
1221 +* supports updates when using refs
1222 * supports error boundaries
1223
1224 src/shared/utils/__tests__/KeyEscapeUtils-test.js
src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
+2 -2
@@ -1205,10 +1205,10 @@ var ReactCompositeComponent = {
1205 * @final
1206 * @private
1207 */
1208 - attachRef: function(ref, component, transaction) {
1208 + attachRef: function(ref, component) {
1209 var inst = this.getPublicInstance();
1210 invariant(inst != null, 'Stateless function components cannot have refs.');
1211 - var publicComponentInstance = component.getPublicInstance(transaction);
1211 + var publicComponentInstance = component.getPublicInstance();
1212 if (__DEV__) {
1213 var componentName = component && component.getName ?
1214 component.getName() : 'a component';
src/renderers/shared/stack/reconciler/ReactOwner.js
+1 -3
@@ -15,7 +15,6 @@
15 var invariant = require('invariant');
16
17 import type { ReactInstance } from 'ReactInstanceType';
18 -import type { Transaction } from 'Transaction';
18
19 /**
20 * @param {?object} object
@@ -74,7 +73,6 @@ var ReactOwner = {
73 component: ReactInstance,
74 ref: string,
75 owner: ReactInstance,
77 - transaction: Transaction,
76 ): void {
77 invariant(
78 isValidOwner(owner),
@@ -83,7 +81,7 @@ var ReactOwner = {
81 '`render` method, or you have multiple copies of React loaded ' +
82 '(details: https://fb.me/react-refs-must-have-owner).'
83 );
86 - owner.attachRef(ref, component, transaction);
84 + owner.attachRef(ref, component);
85 },
86
87 /**
src/renderers/shared/stack/reconciler/ReactReconciler.js
+2 -6
@@ -20,12 +20,8 @@ var warning = require('warning');
20 * Helper to call ReactRef.attachRefs with this composite component, split out
21 * to avoid allocations in the transaction mount-ready queue.
22 */
23 -function attachRefs(transaction) {
24 - ReactRef.attachRefs(
25 - this,
26 - this._currentElement,
27 - transaction,
28 - );
23 +function attachRefs() {
24 + ReactRef.attachRefs(this, this._currentElement);
25 }
26
27 var ReactReconciler = {
src/renderers/shared/stack/reconciler/ReactRef.js
+3 -6
@@ -16,20 +16,18 @@ var ReactOwner = require('ReactOwner');
16
17 import type { ReactInstance } from 'ReactInstanceType';
18 import type { ReactElement } from 'ReactElementType';
19 -import type { Transaction } from 'Transaction';
19
20 var ReactRef = {};
21
23 -function attachRef(ref, component, owner, transaction) {
22 +function attachRef(ref, component, owner) {
23 if (typeof ref === 'function') {
25 - ref(component.getPublicInstance(transaction));
24 + ref(component.getPublicInstance());
25 } else {
26 // Legacy ref
27 ReactOwner.addComponentAsRefTo(
28 component,
29 ref,
30 owner,
32 - transaction,
31 );
32 }
33 }
@@ -46,14 +44,13 @@ function detachRef(ref, component, owner) {
44 ReactRef.attachRefs = function(
45 instance: ReactInstance,
46 element: ReactElement | string | number | null | false,
49 - transaction: Transaction,
47 ): void {
48 if (element === null || typeof element !== 'object') {
49 return;
50 }
51 var ref = element.ref;
52 if (ref != null) {
56 - attachRef(ref, instance, element._owner, transaction);
53 + attachRef(ref, instance, element._owner);
54 }
55 };
56
src/renderers/testing/ReactTestMount.js
+8 -4
@@ -49,19 +49,21 @@ TopLevelWrapper.isReactTopLevelWrapper = true;
49 * Mounts this component and inserts it into the DOM.
50 *
51 * @param {ReactComponent} componentInstance The instance to mount.
52 - * @param {number} rootID ID of the root node.
53 - * @param {number} containerTag container element to mount into.
52 * @param {ReactReconcileTransaction} transaction
53 + * @param {Object} hostParent
54 + * @param {Object} hostContainerInfo
55 */
56 function mountComponentIntoNode(
57 componentInstance,
58 transaction,
59 + hostParent,
60 + hostContainerInfo
61 ) {
62 var image = ReactReconciler.mountComponent(
63 componentInstance,
64 transaction,
65 null,
64 - null,
66 + hostContainerInfo,
67 emptyObject
68 );
69 componentInstance._renderedComponent._topLevelWrapper = componentInstance;
@@ -79,12 +81,14 @@ function batchedMountComponentIntoNode(
81 componentInstance,
82 options,
83 ) {
82 - var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(options);
84 + var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true);
85 var image = transaction.perform(
86 mountComponentIntoNode,
87 null,
88 componentInstance,
89 transaction,
90 + null,
91 + options
92 );
93 ReactUpdates.ReactReconcileTransaction.release(transaction);
94 return image;
src/renderers/testing/ReactTestRenderer.js
+13 -4
@@ -23,6 +23,7 @@ var ReactTestReconcileTransaction = require('ReactTestReconcileTransaction');
23 var ReactUpdates = require('ReactUpdates');
24 var ReactTestTextComponent = require('ReactTestTextComponent');
25 var ReactTestEmptyComponent = require('ReactTestEmptyComponent');
26 +var invariant = require('invariant');
27
28 import type { ReactElement } from 'ReactElementType';
29 import type { ReactInstance } from 'ReactInstanceType';
@@ -53,20 +54,23 @@ class ReactTestComponent {
54 _currentElement: ReactElement;
55 _renderedChildren: null | Object;
56 _topLevelWrapper: null | ReactInstance;
57 + _hostContainerInfo: null | Object;
58
59 constructor(element: ReactElement) {
60 this._currentElement = element;
61 this._renderedChildren = null;
62 this._topLevelWrapper = null;
63 + this._hostContainerInfo = null;
64 }
65
66 mountComponent(
67 transaction: ReactTestReconcileTransaction,
68 nativeParent: null | ReactTestComponent,
66 - nativeContainerInfo: ?null,
69 + hostContainerInfo: Object,
70 context: Object,
71 ) {
72 var element = this._currentElement;
73 + this._hostContainerInfo = hostContainerInfo;
74 // $FlowFixMe https://github.com/facebook/flow/issues/1805
75 this.mountChildren(element.props.children, transaction, context);
76 }
@@ -81,10 +85,15 @@ class ReactTestComponent {
85 this.updateChildren(nextElement.props.children, transaction, context);
86 }
87
84 - getPublicInstance(transaction: ReactTestReconcileTransaction): Object {
88 + getPublicInstance(): Object {
89 var element = this._currentElement;
86 - var options = transaction.getTestOptions();
87 - return options.createNodeMock(element);
90 + var hostContainerInfo = this._hostContainerInfo;
91 + invariant(
92 + hostContainerInfo,
93 + 'hostContainerInfo should be populated before ' +
94 + 'getPublicInstance is called.'
95 + );
96 + return hostContainerInfo.createNodeMock(element);
97 }
98
99 toJSON(): ReactTestRendererJSON {
src/renderers/testing/__tests__/ReactTestRenderer-test.js
+35
@@ -306,6 +306,41 @@ describe('ReactTestRenderer', () => {
306 ]);
307 });
308
309 + it('supports unmounting when using refs', () => {
310 + class Foo extends React.Component {
311 + render() {
312 + return <div ref="foo" />;
313 + }
314 + }
315 + const inst = ReactTestRenderer.create(
316 + <Foo />,
317 + {createNodeMock: () => 'foo'}
318 + );
319 + expect(() => inst.unmount()).not.toThrow();
320 + });
321 +
322 + it('supports updates when using refs', () => {
323 + const log = [];
324 + const createNodeMock = element => {
325 + log.push(element.type);
326 + return element.type;
327 + };
328 + class Foo extends React.Component {
329 + render() {
330 + return this.props.useDiv
331 + ? <div ref="foo" />
332 + : <span ref="foo" />;
333 + }
334 + }
335 + const inst = ReactTestRenderer.create(
336 + <Foo useDiv={true} />,
337 + {createNodeMock}
338 + );
339 + inst.update(<Foo useDiv={false} />);
340 + // It's called with 'div' twice (mounting and unmounting)
341 + expect(log).toEqual(['div', 'div', 'span']);
342 + });
343 +
344 it('supports error boundaries', () => {
345 var log = [];
346 class Angry extends React.Component {