From 5c5d2ec182e4f517b7f3b865d0277ed0dc4f0abd Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Sun, 27 Sep 2015 16:22:51 -0700 Subject: [PATCH] Walk down internal tree to find DOM node This reduces our reliance on hierarchical IDs. --- src/renderers/dom/client/ReactMount.js | 19 -------- src/renderers/dom/client/findDOMNode.js | 47 +++++++++++++----- src/renderers/dom/shared/ReactDOMComponent.js | 4 ++ .../reconciler/ReactCompositeComponent.js | 5 ++ .../shared/reconciler/ReactEmptyComponent.js | 3 -- .../reconciler/ReactEmptyComponentRegistry.js | 48 ------------------- src/shared/utils/ReactNodeTypes.js | 37 ++++++++++++++ 7 files changed, 80 insertions(+), 83 deletions(-) delete mode 100644 src/renderers/shared/reconciler/ReactEmptyComponentRegistry.js create mode 100644 src/shared/utils/ReactNodeTypes.js diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js index aca81fa2b042d..bdde2483c4f5a 100644 --- a/src/renderers/dom/client/ReactMount.js +++ b/src/renderers/dom/client/ReactMount.js @@ -17,9 +17,7 @@ var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); var ReactCurrentOwner = require('ReactCurrentOwner'); var ReactDOMContainerInfo = require('ReactDOMContainerInfo'); var ReactElement = require('ReactElement'); -var ReactEmptyComponentRegistry = require('ReactEmptyComponentRegistry'); var ReactInstanceHandles = require('ReactInstanceHandles'); -var ReactInstanceMap = require('ReactInstanceMap'); var ReactMarkupChecksum = require('ReactMarkupChecksum'); var ReactPerf = require('ReactPerf'); var ReactReconciler = require('ReactReconciler'); @@ -175,21 +173,6 @@ function getNode(id) { } } -/** - * Finds the node with the supplied public React instance. - * - * @param {*} instance A public React instance. - * @return {?DOMElement} DOM node with the suppled `id`. - * @internal - */ -function getNodeFromInstance(instance) { - var id = ReactInstanceMap.get(instance)._rootNodeID; - if (ReactEmptyComponentRegistry.isNullComponentID(id)) { - return null; - } - return getNode(id); -} - /** * A node is "valid" if it is contained by a currently mounted container. * @@ -1073,8 +1056,6 @@ var ReactMount = { getNode: getNode, - getNodeFromInstance: getNodeFromInstance, - isValid: isValid, purgeID: purgeID, diff --git a/src/renderers/dom/client/findDOMNode.js b/src/renderers/dom/client/findDOMNode.js index 870284446a0c4..27e8c40ca82c5 100644 --- a/src/renderers/dom/client/findDOMNode.js +++ b/src/renderers/dom/client/findDOMNode.js @@ -13,12 +13,27 @@ 'use strict'; var ReactCurrentOwner = require('ReactCurrentOwner'); +var ReactDOMComponent = require('ReactDOMComponent'); var ReactInstanceMap = require('ReactInstanceMap'); -var ReactMount = require('ReactMount'); +var ReactNodeTypes = require('ReactNodeTypes'); var invariant = require('invariant'); var warning = require('warning'); +function getNativeComponentFromComposite(inst) { + var type; + + while ((type = inst._renderedNodeType) === ReactNodeTypes.COMPOSITE) { + inst = inst._renderedComponent; + } + + if (type === ReactNodeTypes.NATIVE) { + return inst._renderedComponent; + } else if (type === ReactNodeTypes.EMPTY) { + return null; + } +} + /** * Returns the DOM node rendered by this element. * @@ -47,19 +62,25 @@ function findDOMNode(componentOrElement) { if (componentOrElement.nodeType === 1) { return componentOrElement; } - if (ReactInstanceMap.has(componentOrElement)) { - return ReactMount.getNodeFromInstance(componentOrElement); + + var inst = ReactInstanceMap.get(componentOrElement); + if (inst) { + inst = getNativeComponentFromComposite(inst); + return inst ? ReactDOMComponent.getNodeFromInstance(inst) : null; + } + + if (typeof componentOrElement.render === 'function') { + invariant( + false, + 'findDOMNode was called on an unmounted component.' + ); + } else { + invariant( + false, + 'Element appears to be neither ReactComponent nor DOMNode (keys: %s)', + Object.keys(componentOrElement) + ); } - invariant( - componentOrElement.render == null || - typeof componentOrElement.render !== 'function', - 'findDOMNode was called on an unmounted component.' - ); - invariant( - false, - 'Element appears to be neither ReactComponent nor DOMNode (keys: %s)', - Object.keys(componentOrElement) - ); } module.exports = findDOMNode; diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index cf7c1587c1bc0..b530b6374bf46 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -1173,4 +1173,8 @@ assign( ReactMultiChild.Mixin ); +assign(ReactDOMComponent, { + getNodeFromInstance: getNode, +}); + module.exports = ReactDOMComponent; diff --git a/src/renderers/shared/reconciler/ReactCompositeComponent.js b/src/renderers/shared/reconciler/ReactCompositeComponent.js index 0e30a0128d8eb..4cb8633c91b92 100644 --- a/src/renderers/shared/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/reconciler/ReactCompositeComponent.js @@ -15,6 +15,7 @@ var ReactComponentEnvironment = require('ReactComponentEnvironment'); var ReactCurrentOwner = require('ReactCurrentOwner'); var ReactElement = require('ReactElement'); var ReactInstanceMap = require('ReactInstanceMap'); +var ReactNodeTypes = require('ReactNodeTypes'); var ReactPerf = require('ReactPerf'); var ReactPropTypeLocations = require('ReactPropTypeLocations'); var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames'); @@ -105,6 +106,7 @@ var ReactCompositeComponentMixin = { this._pendingReplaceState = false; this._pendingForceUpdate = false; + this._renderedNodeType = null; this._renderedComponent = null; this._context = null; @@ -290,6 +292,7 @@ var ReactCompositeComponentMixin = { renderedElement = this._renderValidatedComponent(); } + this._renderedNodeType = ReactNodeTypes.getType(renderedElement); this._renderedComponent = this._instantiateReactComponent( renderedElement ); @@ -327,6 +330,7 @@ var ReactCompositeComponentMixin = { } ReactReconciler.unmountComponent(this._renderedComponent); + this._renderedNodeType = null; this._renderedComponent = null; this._instance = null; @@ -752,6 +756,7 @@ var ReactCompositeComponentMixin = { ReactReconciler.unmountComponent(prevComponentInstance); + this._renderedNodeType = ReactNodeTypes.getType(nextRenderedElement); this._renderedComponent = this._instantiateReactComponent( nextRenderedElement ); diff --git a/src/renderers/shared/reconciler/ReactEmptyComponent.js b/src/renderers/shared/reconciler/ReactEmptyComponent.js index 48430d6009722..c9df64d031680 100644 --- a/src/renderers/shared/reconciler/ReactEmptyComponent.js +++ b/src/renderers/shared/reconciler/ReactEmptyComponent.js @@ -12,7 +12,6 @@ 'use strict'; var ReactElement = require('ReactElement'); -var ReactEmptyComponentRegistry = require('ReactEmptyComponentRegistry'); var ReactReconciler = require('ReactReconciler'); var assign = require('Object.assign'); @@ -40,7 +39,6 @@ assign(ReactEmptyComponent.prototype, { nativeContainerInfo, context ) { - ReactEmptyComponentRegistry.registerNullComponentID(rootID); this._rootNodeID = rootID; return ReactReconciler.mountComponent( this._renderedComponent, @@ -58,7 +56,6 @@ assign(ReactEmptyComponent.prototype, { }, unmountComponent: function(rootID, transaction, context) { ReactReconciler.unmountComponent(this._renderedComponent); - ReactEmptyComponentRegistry.deregisterNullComponentID(this._rootNodeID); this._rootNodeID = null; this._renderedComponent = null; }, diff --git a/src/renderers/shared/reconciler/ReactEmptyComponentRegistry.js b/src/renderers/shared/reconciler/ReactEmptyComponentRegistry.js deleted file mode 100644 index 43f1808f62eb6..0000000000000 --- a/src/renderers/shared/reconciler/ReactEmptyComponentRegistry.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2014-2015, 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 ReactEmptyComponentRegistry - */ - -'use strict'; - -// This registry keeps track of the React IDs of the components that rendered to -// `null` (in reality a placeholder such as `noscript`) -var nullComponentIDsRegistry = {}; - -/** - * @param {string} id Component's `_rootNodeID`. - * @return {boolean} True if the component is rendered to null. - */ -function isNullComponentID(id) { - return !!nullComponentIDsRegistry[id]; -} - -/** - * Mark the component as having rendered to null. - * @param {string} id Component's `_rootNodeID`. - */ -function registerNullComponentID(id) { - nullComponentIDsRegistry[id] = true; -} - -/** - * Unmark the component as having rendered to null: it renders to something now. - * @param {string} id Component's `_rootNodeID`. - */ -function deregisterNullComponentID(id) { - delete nullComponentIDsRegistry[id]; -} - -var ReactEmptyComponentRegistry = { - isNullComponentID: isNullComponentID, - registerNullComponentID: registerNullComponentID, - deregisterNullComponentID: deregisterNullComponentID, -}; - -module.exports = ReactEmptyComponentRegistry; diff --git a/src/shared/utils/ReactNodeTypes.js b/src/shared/utils/ReactNodeTypes.js new file mode 100644 index 0000000000000..94c88576dc7ae --- /dev/null +++ b/src/shared/utils/ReactNodeTypes.js @@ -0,0 +1,37 @@ +/** + * Copyright 2013-2015, 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 ReactNodeTypes + */ + +'use strict'; + +var ReactElement = require('ReactElement'); + +var invariant = require('invariant'); + +var ReactNodeTypes = { + NATIVE: 0, + COMPOSITE: 1, + EMPTY: 2, + + getType: function(node) { + if (node === null || node === false) { + return ReactNodeTypes.EMPTY; + } else if (ReactElement.isValidElement(node)) { + if (typeof node.type === 'function') { + return ReactNodeTypes.COMPOSITE; + } else { + return ReactNodeTypes.NATIVE; + } + } + invariant(false, 'Unexpected node: %s', node); + }, +}; + +module.exports = ReactNodeTypes;