mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
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
(cherry picked from commit e43aaab254)
This commit is contained in:
committed by
Paul O’Shannessy
parent
28e51005dc
commit
7df71a2525
@@ -1100,10 +1100,10 @@ var ReactCompositeComponent = {
|
||||
* @final
|
||||
* @private
|
||||
*/
|
||||
attachRef: function(ref, component, transaction) {
|
||||
attachRef: function(ref, component) {
|
||||
var inst = this.getPublicInstance();
|
||||
invariant(inst != null, 'Stateless function components cannot have refs.');
|
||||
var publicComponentInstance = component.getPublicInstance(transaction);
|
||||
var publicComponentInstance = component.getPublicInstance();
|
||||
if (__DEV__) {
|
||||
var componentName = component && component.getName ?
|
||||
component.getName() : 'a component';
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
var invariant = require('invariant');
|
||||
|
||||
import type { ReactInstance } from 'ReactInstanceType';
|
||||
import type { Transaction } from 'Transaction';
|
||||
|
||||
/**
|
||||
* @param {?object} object
|
||||
@@ -74,7 +73,6 @@ var ReactOwner = {
|
||||
component: ReactInstance,
|
||||
ref: string,
|
||||
owner: ReactInstance,
|
||||
transaction: Transaction,
|
||||
): void {
|
||||
invariant(
|
||||
isValidOwner(owner),
|
||||
@@ -83,7 +81,7 @@ var ReactOwner = {
|
||||
'`render` method, or you have multiple copies of React loaded ' +
|
||||
'(details: https://fb.me/react-refs-must-have-owner).'
|
||||
);
|
||||
owner.attachRef(ref, component, transaction);
|
||||
owner.attachRef(ref, component);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,12 +20,8 @@ var warning = require('warning');
|
||||
* Helper to call ReactRef.attachRefs with this composite component, split out
|
||||
* to avoid allocations in the transaction mount-ready queue.
|
||||
*/
|
||||
function attachRefs(transaction) {
|
||||
ReactRef.attachRefs(
|
||||
this,
|
||||
this._currentElement,
|
||||
transaction,
|
||||
);
|
||||
function attachRefs() {
|
||||
ReactRef.attachRefs(this, this._currentElement);
|
||||
}
|
||||
|
||||
var ReactReconciler = {
|
||||
|
||||
@@ -16,20 +16,18 @@ var ReactOwner = require('ReactOwner');
|
||||
|
||||
import type { ReactInstance } from 'ReactInstanceType';
|
||||
import type { ReactElement } from 'ReactElementType';
|
||||
import type { Transaction } from 'Transaction';
|
||||
|
||||
var ReactRef = {};
|
||||
|
||||
function attachRef(ref, component, owner, transaction) {
|
||||
function attachRef(ref, component, owner) {
|
||||
if (typeof ref === 'function') {
|
||||
ref(component.getPublicInstance(transaction));
|
||||
ref(component.getPublicInstance());
|
||||
} else {
|
||||
// Legacy ref
|
||||
ReactOwner.addComponentAsRefTo(
|
||||
component,
|
||||
ref,
|
||||
owner,
|
||||
transaction,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -46,14 +44,13 @@ function detachRef(ref, component, owner) {
|
||||
ReactRef.attachRefs = function(
|
||||
instance: ReactInstance,
|
||||
element: ReactElement | string | number | null | false,
|
||||
transaction: Transaction,
|
||||
): void {
|
||||
if (element === null || typeof element !== 'object') {
|
||||
return;
|
||||
}
|
||||
var ref = element.ref;
|
||||
if (ref != null) {
|
||||
attachRef(ref, instance, element._owner, transaction);
|
||||
attachRef(ref, instance, element._owner);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -51,19 +51,21 @@ TopLevelWrapper.isReactTopLevelWrapper = true;
|
||||
* Mounts this component and inserts it into the DOM.
|
||||
*
|
||||
* @param {ReactComponent} componentInstance The instance to mount.
|
||||
* @param {number} rootID ID of the root node.
|
||||
* @param {number} containerTag container element to mount into.
|
||||
* @param {ReactReconcileTransaction} transaction
|
||||
* @param {Object} hostParent
|
||||
* @param {Object} hostContainerInfo
|
||||
*/
|
||||
function mountComponentIntoNode(
|
||||
componentInstance,
|
||||
transaction,
|
||||
hostParent,
|
||||
hostContainerInfo
|
||||
) {
|
||||
var image = ReactReconciler.mountComponent(
|
||||
componentInstance,
|
||||
transaction,
|
||||
null,
|
||||
null,
|
||||
hostContainerInfo,
|
||||
emptyObject
|
||||
);
|
||||
componentInstance._renderedComponent._topLevelWrapper = componentInstance;
|
||||
@@ -81,12 +83,14 @@ function batchedMountComponentIntoNode(
|
||||
componentInstance,
|
||||
options,
|
||||
) {
|
||||
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(options);
|
||||
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true);
|
||||
var image = transaction.perform(
|
||||
mountComponentIntoNode,
|
||||
null,
|
||||
componentInstance,
|
||||
transaction,
|
||||
null,
|
||||
options
|
||||
);
|
||||
ReactUpdates.ReactReconcileTransaction.release(transaction);
|
||||
return image;
|
||||
|
||||
@@ -22,6 +22,7 @@ var ReactTestReconcileTransaction = require('ReactTestReconcileTransaction');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
var ReactTestTextComponent = require('ReactTestTextComponent');
|
||||
var ReactTestEmptyComponent = require('ReactTestEmptyComponent');
|
||||
var invariant = require('invariant');
|
||||
|
||||
import type { ReactElement } from 'ReactElementType';
|
||||
import type { ReactInstance } from 'ReactInstanceType';
|
||||
@@ -52,20 +53,23 @@ class ReactTestComponent {
|
||||
_currentElement: ReactElement;
|
||||
_renderedChildren: null | Object;
|
||||
_topLevelWrapper: null | ReactInstance;
|
||||
_hostContainerInfo: null | Object;
|
||||
|
||||
constructor(element: ReactElement) {
|
||||
this._currentElement = element;
|
||||
this._renderedChildren = null;
|
||||
this._topLevelWrapper = null;
|
||||
this._hostContainerInfo = null;
|
||||
}
|
||||
|
||||
mountComponent(
|
||||
transaction: ReactTestReconcileTransaction,
|
||||
nativeParent: null | ReactTestComponent,
|
||||
nativeContainerInfo: ?null,
|
||||
hostContainerInfo: Object,
|
||||
context: Object,
|
||||
) {
|
||||
var element = this._currentElement;
|
||||
this._hostContainerInfo = hostContainerInfo;
|
||||
// $FlowFixMe https://github.com/facebook/flow/issues/1805
|
||||
this.mountChildren(element.props.children, transaction, context);
|
||||
}
|
||||
@@ -80,10 +84,15 @@ class ReactTestComponent {
|
||||
this.updateChildren(nextElement.props.children, transaction, context);
|
||||
}
|
||||
|
||||
getPublicInstance(transaction: ReactTestReconcileTransaction): Object {
|
||||
getPublicInstance(): Object {
|
||||
var element = this._currentElement;
|
||||
var options = transaction.getTestOptions();
|
||||
return options.createNodeMock(element);
|
||||
var hostContainerInfo = this._hostContainerInfo;
|
||||
invariant(
|
||||
hostContainerInfo,
|
||||
'hostContainerInfo should be populated before ' +
|
||||
'getPublicInstance is called.'
|
||||
);
|
||||
return hostContainerInfo.createNodeMock(element);
|
||||
}
|
||||
|
||||
toJSON(): ReactTestRendererJSON {
|
||||
|
||||
@@ -306,6 +306,41 @@ describe('ReactTestRenderer', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('supports unmounting when using refs', () => {
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return <div ref="foo" />;
|
||||
}
|
||||
}
|
||||
const inst = ReactTestRenderer.create(
|
||||
<Foo />,
|
||||
{createNodeMock: () => 'foo'}
|
||||
);
|
||||
expect(() => inst.unmount()).not.toThrow();
|
||||
});
|
||||
|
||||
it('supports updates when using refs', () => {
|
||||
const log = [];
|
||||
const createNodeMock = element => {
|
||||
log.push(element.type);
|
||||
return element.type;
|
||||
};
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return this.props.useDiv
|
||||
? <div ref="foo" />
|
||||
: <span ref="foo" />;
|
||||
}
|
||||
}
|
||||
const inst = ReactTestRenderer.create(
|
||||
<Foo useDiv={true} />,
|
||||
{createNodeMock}
|
||||
);
|
||||
inst.update(<Foo useDiv={false} />);
|
||||
// It's called with 'div' twice (mounting and unmounting)
|
||||
expect(log).toEqual(['div', 'div', 'span']);
|
||||
});
|
||||
|
||||
it('supports error boundaries', () => {
|
||||
var log = [];
|
||||
class Angry extends React.Component {
|
||||
|
||||
Reference in New Issue
Block a user