From 298d0c3e05e618b7c46b7da0a4e214a20ead3670 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Sat, 22 Oct 2016 17:41:46 -0700 Subject: [PATCH] Implement findDOMNode for Fiber This is the naive implementation that doesn't cover the case where there are two diverging fibers, or if this tree is unmounted. --- src/renderers/dom/fiber/ReactDOMFiber.js | 12 +++++ .../shared/fiber/ReactFiberReconciler.js | 21 ++++++-- .../shared/fiber/ReactFiberTreeReflection.js | 54 +++++++++++++++++++ 3 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/renderers/shared/fiber/ReactFiberTreeReflection.js diff --git a/src/renderers/dom/fiber/ReactDOMFiber.js b/src/renderers/dom/fiber/ReactDOMFiber.js index f1f7163bc6..9a5473bfd9 100644 --- a/src/renderers/dom/fiber/ReactDOMFiber.js +++ b/src/renderers/dom/fiber/ReactDOMFiber.js @@ -146,6 +146,18 @@ var ReactDOM = { } }, + findDOMNode(componentOrElement : Element | ?ReactComponent) : null | Element | Text { + if (componentOrElement == null) { + return null; + } + // Unsound duck typing. + const component = (componentOrElement : any); + if (component.nodeType === 1) { + return component; + } + return DOMRenderer.findHostInstance(component); + }, + }; module.exports = ReactDOM; diff --git a/src/renderers/shared/fiber/ReactFiberReconciler.js b/src/renderers/shared/fiber/ReactFiberReconciler.js index be3b57fbb1..f775e6b1f0 100644 --- a/src/renderers/shared/fiber/ReactFiberReconciler.js +++ b/src/renderers/shared/fiber/ReactFiberReconciler.js @@ -24,6 +24,8 @@ if (__DEV__) { var ReactFiberInstrumentation = require('ReactFiberInstrumentation'); } +var { findCurrentHostFiber } = require('ReactFiberTreeReflection'); + type Deadline = { timeRemaining : () => number }; @@ -58,17 +60,20 @@ export type HostConfig = { type OpaqueNode = Fiber; -export type Reconciler = { +export type Reconciler = { mountContainer(element : ReactElement, containerInfo : C) : OpaqueNode, updateContainer(element : ReactElement, container : OpaqueNode) : void, unmountContainer(container : OpaqueNode) : void, performWithPriority(priorityLevel : PriorityLevel, fn : Function) : void, // Used to extract the return value from the initial render. Legacy API. - getPublicRootInstance(container : OpaqueNode) : (ReactComponent | I | null), + getPublicRootInstance(container : OpaqueNode) : (ReactComponent | TI | I | null), + + // Use for findDOMNode/findHostNode. Legacy API. + findHostInstance(component : ReactComponent) : I | TI | null, }; -module.exports = function(config : HostConfig) : Reconciler { +module.exports = function(config : HostConfig) : Reconciler { var { scheduleWork, performWithPriority } = ReactFiberScheduler(config); @@ -122,7 +127,7 @@ module.exports = function(config : HostConfig) : performWithPriority, - getPublicRootInstance(container : OpaqueNode) : (ReactComponent | I | null) { + getPublicRootInstance(container : OpaqueNode) : (ReactComponent | I | TI | null) { const root : FiberRoot = (container.stateNode : any); const containerFiber = root.current; if (!containerFiber.child) { @@ -131,6 +136,14 @@ module.exports = function(config : HostConfig) : return containerFiber.child.stateNode; }, + findHostInstance(component : ReactComponent) : I | TI | null { + const fiber = findCurrentHostFiber(component); + if (!fiber) { + return null; + } + return fiber.stateNode; + }, + }; }; diff --git a/src/renderers/shared/fiber/ReactFiberTreeReflection.js b/src/renderers/shared/fiber/ReactFiberTreeReflection.js new file mode 100644 index 0000000000..8fddb2fec1 --- /dev/null +++ b/src/renderers/shared/fiber/ReactFiberTreeReflection.js @@ -0,0 +1,54 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactFiberTreeReflection + * @flow + */ + +'use strict'; + +import type { Fiber } from 'ReactFiber'; + +var ReactInstanceMap = require('ReactInstanceMap'); + +var { + ClassComponent, + HostContainer, + HostComponent, + HostText, +} = require('ReactTypeOfWork'); + +exports.findCurrentHostFiber = function(component : ReactComponent) : Fiber | null { + var parent : ?Fiber = ReactInstanceMap.get(component); + if (!parent) { + return null; + } + + // TODO: This search is incomplete because this could be one of two possible fibers. + let node : Fiber = parent; + while (true) { + if (node.tag === HostComponent || node.tag === HostText) { + return node; + } else if (node.child) { + // TODO: Coroutines need to visit the stateNode. + node = node.child; + continue; + } + if (node === parent) { + return null; + } + while (!node.sibling) { + if (!node.return || node.return === parent) { + return null; + } + node = node.return; + } + node = node.sibling; + } + return null; +};