diff --git a/scripts/fiber/tests-passing.txt b/scripts/fiber/tests-passing.txt
index b9c1d827a2bef..111ea1692e200 100644
--- a/scripts/fiber/tests-passing.txt
+++ b/scripts/fiber/tests-passing.txt
@@ -610,6 +610,7 @@ src/renderers/__tests__/refs-test.js
* coerces numbers to strings
* attaches, detaches from fiber component with stack layer
* attaches, detaches from stack component with fiber layer
+* attaches and detaches root refs
src/renderers/art/__tests__/ReactART-test.js
* should have the correct lifecycle state
@@ -1972,6 +1973,7 @@ src/renderers/testing/__tests__/ReactTestRenderer-test.js
* can update text nodes
* toTree() renders simple components returning host components
* toTree() handles null rendering components
+* root instance and createNodeMock ref return the same value
* toTree() renders complicated trees of composites and hosts
* can update text nodes when rendered as root
* can render and update root fragments
diff --git a/src/renderers/__tests__/refs-test.js b/src/renderers/__tests__/refs-test.js
index 202a101f1c8dc..05727c764d2d1 100644
--- a/src/renderers/__tests__/refs-test.js
+++ b/src/renderers/__tests__/refs-test.js
@@ -409,3 +409,87 @@ describe('string refs between fiber and stack', () => {
}
});
});
+
+describe('root level refs', () => {
+ beforeEach(() => {
+ var ReactFeatureFlags = require('ReactFeatureFlags');
+ ReactFeatureFlags.disableNewFiberFeatures = false;
+ });
+
+ it('attaches and detaches root refs', () => {
+ var ReactDOM = require('react-dom');
+ var inst = null;
+
+ // host node
+ var ref = jest.fn(value => (inst = value));
+ var container = document.createElement('div');
+ var result = ReactDOM.render(
, container);
+ expect(ref).toHaveBeenCalledTimes(1);
+ expect(ref.mock.calls[0][0]).toBeInstanceOf(HTMLDivElement);
+ expect(result).toBe(ref.mock.calls[0][0]);
+ ReactDOM.unmountComponentAtNode(container);
+ expect(ref).toHaveBeenCalledTimes(2);
+ expect(ref.mock.calls[1][0]).toBe(null);
+
+ // composite
+ class Comp extends React.Component {
+ method() {
+ return true;
+ }
+ render() {
+ return Comp
;
+ }
+ }
+
+ inst = null;
+ ref = jest.fn(value => (inst = value));
+ result = ReactDOM.render(, container);
+
+ expect(ref).toHaveBeenCalledTimes(1);
+ expect(inst).toBeInstanceOf(Comp);
+ expect(result).toBe(inst);
+
+ // ensure we have the correct instance
+ expect(result.method()).toBe(true);
+ expect(inst.method()).toBe(true);
+
+ ReactDOM.unmountComponentAtNode(container);
+ expect(ref).toHaveBeenCalledTimes(2);
+ expect(ref.mock.calls[1][0]).toBe(null);
+
+ if (ReactDOMFeatureFlags.useFiber) {
+ // fragment
+ inst = null;
+ ref = jest.fn(value => (inst = value));
+ var divInst = null;
+ var ref2 = jest.fn(value => (divInst = value));
+ result = ReactDOM.render(
+ [, 5, Hello
],
+ container,
+ );
+
+ // first call should be `Comp`
+ expect(ref).toHaveBeenCalledTimes(1);
+ expect(ref.mock.calls[0][0]).toBeInstanceOf(Comp);
+ expect(result).toBe(ref.mock.calls[0][0]);
+
+ expect(ref2).toHaveBeenCalledTimes(1);
+ expect(divInst).toBeInstanceOf(HTMLDivElement);
+ expect(result).not.toBe(divInst);
+
+ ReactDOM.unmountComponentAtNode(container);
+ expect(ref).toHaveBeenCalledTimes(2);
+ expect(ref.mock.calls[1][0]).toBe(null);
+ expect(ref2).toHaveBeenCalledTimes(2);
+ expect(ref2.mock.calls[1][0]).toBe(null);
+
+ // null
+ result = ReactDOM.render(null, container);
+ expect(result).toBe(null);
+
+ // primitives
+ result = ReactDOM.render(5, container);
+ expect(result).toBeInstanceOf(Text);
+ }
+ });
+});
diff --git a/src/renderers/shared/fiber/ReactFiberCommitWork.js b/src/renderers/shared/fiber/ReactFiberCommitWork.js
index 2703f065897f9..7fe0efad7233f 100644
--- a/src/renderers/shared/fiber/ReactFiberCommitWork.js
+++ b/src/renderers/shared/fiber/ReactFiberCommitWork.js
@@ -518,8 +518,14 @@ module.exports = function(
function commitAttachRef(finishedWork: Fiber) {
const ref = finishedWork.ref;
if (ref !== null) {
- const instance = getPublicInstance(finishedWork.stateNode);
- ref(instance);
+ const instance = finishedWork.stateNode;
+ switch (finishedWork.tag) {
+ case HostComponent:
+ ref(getPublicInstance(instance));
+ break;
+ default:
+ ref(instance);
+ }
}
}
diff --git a/src/renderers/shared/fiber/ReactFiberReconciler.js b/src/renderers/shared/fiber/ReactFiberReconciler.js
index 14426385a0152..a7e41318647cd 100644
--- a/src/renderers/shared/fiber/ReactFiberReconciler.js
+++ b/src/renderers/shared/fiber/ReactFiberReconciler.js
@@ -28,6 +28,7 @@ var {
} = require('ReactFiberContext');
var {createFiberRoot} = require('ReactFiberRoot');
var ReactFiberScheduler = require('ReactFiberScheduler');
+var {HostComponent} = require('ReactTypeOfWork');
if (__DEV__) {
var warning = require('fbjs/lib/warning');
@@ -167,6 +168,8 @@ getContextForSubtree._injectFiber(function(fiber: Fiber) {
module.exports = function(
config: HostConfig,
): Reconciler {
+ var {getPublicInstance} = config;
+
var {
scheduleUpdate,
getPriorityContext,
@@ -269,15 +272,20 @@ module.exports = function(
getPublicRootInstance(
container: OpaqueRoot,
- ): ReactComponent | I | TI | null {
+ ): ReactComponent | PI | null {
const containerFiber = container.current;
if (!containerFiber.child) {
return null;
}
- return containerFiber.child.stateNode;
+ switch (containerFiber.child.tag) {
+ case HostComponent:
+ return getPublicInstance(containerFiber.child.stateNode);
+ default:
+ return containerFiber.child.stateNode;
+ }
},
- findHostInstance(fiber: Fiber): I | TI | null {
+ findHostInstance(fiber: Fiber): PI | null {
const hostFiber = findCurrentHostFiber(fiber);
if (hostFiber === null) {
return null;
diff --git a/src/renderers/testing/ReactTestRendererFiber.js b/src/renderers/testing/ReactTestRendererFiber.js
index 19c2fee0b2d2f..0fa7daced485c 100644
--- a/src/renderers/testing/ReactTestRendererFiber.js
+++ b/src/renderers/testing/ReactTestRendererFiber.js
@@ -219,7 +219,7 @@ var TestRenderer = ReactFiberReconciler({
useSyncScheduling: true,
- getPublicInstance(inst) {
+ getPublicInstance(inst: Instance | TextInstance): * {
switch (inst.tag) {
case 'INSTANCE':
const createNodeMock = inst.rootContainerInstance.createNodeMock;
diff --git a/src/renderers/testing/__tests__/ReactTestRenderer-test.js b/src/renderers/testing/__tests__/ReactTestRenderer-test.js
index c4a0735341bf1..81b0ea4c2bd25 100644
--- a/src/renderers/testing/__tests__/ReactTestRenderer-test.js
+++ b/src/renderers/testing/__tests__/ReactTestRenderer-test.js
@@ -568,6 +568,17 @@ describe('ReactTestRenderer', () => {
});
});
+ it('root instance and createNodeMock ref return the same value', () => {
+ var createNodeMock = ref => ({node: ref});
+ var refInst = null;
+ var renderer = ReactTestRenderer.create(
+ (refInst = ref)} />,
+ {createNodeMock},
+ );
+ var root = renderer.getInstance();
+ expect(root).toEqual(refInst);
+ });
+
it('toTree() renders complicated trees of composites and hosts', () => {
// SFC returning host. no children props.
var Qoo = () => Hello World!;