diff --git a/src/renderers/dom/client/ReactDOMComponentTree.js b/src/renderers/dom/client/ReactDOMComponentTree.js index dfd128ac2e..53d29d2dca 100644 --- a/src/renderers/dom/client/ReactDOMComponentTree.js +++ b/src/renderers/dom/client/ReactDOMComponentTree.js @@ -77,7 +77,7 @@ function precacheChildNodes(inst, node) { continue; } var childInst = children[name]; - var childID = childInst._rootNodeID; + var childID = getRenderedNativeOrTextFromComponent(childInst)._domID; if (childID == null) { // We're currently unmounting this child in ReactMultiChild; skip it. continue; @@ -85,7 +85,7 @@ function precacheChildNodes(inst, node) { // We assume the child nodes are in the same order as the child instances. for (; childNode !== null; childNode = childNode.nextSibling) { if (childNode.nodeType === 1 && - childNode.getAttribute(ATTR_NAME) === childID) { + childNode.getAttribute(ATTR_NAME) === String(childID)) { precacheNode(childInst, childNode); continue outer; } diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js index 88ed3a01a8..4db71492bb 100644 --- a/src/renderers/dom/client/ReactMount.js +++ b/src/renderers/dom/client/ReactMount.js @@ -11,7 +11,6 @@ 'use strict'; -var ClientReactRootIndex = require('ClientReactRootIndex'); var DOMLazyTree = require('DOMLazyTree'); var DOMProperty = require('DOMProperty'); var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); @@ -20,7 +19,6 @@ var ReactDOMComponentTree = require('ReactDOMComponentTree'); var ReactDOMContainerInfo = require('ReactDOMContainerInfo'); var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactElement = require('ReactElement'); -var ReactInstanceHandles = require('ReactInstanceHandles'); var ReactMarkupChecksum = require('ReactMarkupChecksum'); var ReactPerf = require('ReactPerf'); var ReactReconciler = require('ReactReconciler'); @@ -35,23 +33,12 @@ var shouldUpdateReactComponent = require('shouldUpdateReactComponent'); var warning = require('warning'); var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME; +var ROOT_ATTR_NAME = DOMProperty.ROOT_ATTRIBUTE_NAME; var ELEMENT_NODE_TYPE = 1; var DOC_NODE_TYPE = 9; var DOCUMENT_FRAGMENT_NODE_TYPE = 11; - -/** Mapping from reactRootID to React component instance. */ -var instancesByReactRootID = {}; - -/** Mapping from reactRootID to `container` nodes. */ -var containersByReactRootID = {}; - -if (__DEV__) { - /** __DEV__-only mapping from reactRootID to root elements. */ - var rootElementsByReactRootID = {}; -} - /** * Finds the index of the first character * that's not common between the two given strings. @@ -85,20 +72,11 @@ function getReactRootElementInContainer(container) { } } -/** - * @param {DOMElement} container DOM element that may contain a React component. - * @return {?string} A "reactRoot" ID, if a React component is rendered. - */ -function getReactRootID(container) { - var rootElement = getReactRootElementInContainer(container); - return rootElement && internalGetID(rootElement); -} - function internalGetID(node) { // If node is something like a window, document, or text node, none of // which support attributes or a .getAttribute method, gracefully return // the empty string, as if the attribute were missing. - return node && node.getAttribute && node.getAttribute(ATTR_NAME) || ''; + return node.getAttribute && node.getAttribute(ATTR_NAME) || ''; } /** @@ -111,7 +89,7 @@ function internalGetID(node) { * @param {boolean} shouldReuseMarkup If true, do not insert markup */ function mountComponentIntoNode( - componentInstance, + wrapperInstance, rootID, container, transaction, @@ -119,18 +97,18 @@ function mountComponentIntoNode( context ) { var markup = ReactReconciler.mountComponent( - componentInstance, + wrapperInstance, rootID, transaction, null, - ReactDOMContainerInfo(container), + ReactDOMContainerInfo(wrapperInstance, container), context ); - componentInstance._renderedComponent._topLevelWrapper = componentInstance; + wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance; ReactMount._mountImageIntoNode( markup, container, - componentInstance, + wrapperInstance, shouldReuseMarkup, transaction ); @@ -200,10 +178,21 @@ function unmountComponentFromNode(instance, container) { * rendered by React but is not a root element. * @internal */ -function hasNonRootReactChild(node) { - var reactRootID = getReactRootID(node); - return reactRootID ? reactRootID !== - ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID) : false; +function hasNonRootReactChild(container) { + var rootElement = getReactRootElementInContainer(container); + return !!( + rootElement && + internalGetID(rootElement) && + !rootElement.hasAttribute(ROOT_ATTR_NAME) + ); +} + +function getTopLevelWrapperInContainer(container) { + var rootEl = getReactRootElementInContainer(container); + var prevNativeInstance = + rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl); + return !prevNativeInstance || prevNativeInstance._nativeParent ? null : + prevNativeInstance._nativeContainerInfo._topLevelWrapper; } /** @@ -243,9 +232,6 @@ var ReactMount = { TopLevelWrapper: TopLevelWrapper, - /** Exposed for debugging purposes **/ - _instancesByReactRootID: instancesByReactRootID, - /** * This is a hook provided to support rendering React components while * ensuring that the apparent scroll position of its `container` does not @@ -277,39 +263,9 @@ var ReactMount = { } }); - if (__DEV__) { - // Record the root element in case it later gets transplanted. - rootElementsByReactRootID[getReactRootID(container)] = - getReactRootElementInContainer(container); - } - return prevComponent; }, - /** - * Register a component into the instance map and starts scroll value - * monitoring - * @param {ReactComponent} nextComponent component instance to render - * @param {DOMElement} container container to render into - * @return {string} reactRoot ID prefix - */ - _registerComponent: function(nextComponent, container) { - invariant( - container && ( - container.nodeType === ELEMENT_NODE_TYPE || - container.nodeType === DOC_NODE_TYPE || - container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE - ), - '_registerComponent(...): Target container is not a DOM element.' - ); - - ReactBrowserEventEmitter.ensureScrollValueMonitoring(); - - var reactRootID = ReactMount.registerContainer(container); - instancesByReactRootID[reactRootID] = nextComponent; - return reactRootID; - }, - /** * Render a new component into the DOM. * @param {ReactElement} nextElement element to render @@ -336,12 +292,18 @@ var ReactMount = { 'ReactCompositeComponent' ); - var componentInstance = instantiateReactComponent(nextElement, null); - var reactRootID = ReactMount._registerComponent( - componentInstance, - container + invariant( + container && ( + container.nodeType === ELEMENT_NODE_TYPE || + container.nodeType === DOC_NODE_TYPE || + container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE + ), + '_registerComponent(...): Target container is not a DOM element.' ); + ReactBrowserEventEmitter.ensureScrollValueMonitoring(); + var componentInstance = instantiateReactComponent(nextElement, null); + // The initial render is synchronous but any updates that happen during // rendering, in componentWillMount or componentDidMount, will be batched // according to the current batching strategy. @@ -349,18 +311,12 @@ var ReactMount = { ReactUpdates.batchedUpdates( batchedMountComponentIntoNode, componentInstance, - reactRootID, + '', container, shouldReuseMarkup, context ); - if (__DEV__) { - // Record the root element in case it later gets transplanted. - rootElementsByReactRootID[reactRootID] = - getReactRootElementInContainer(container); - } - return componentInstance; }, @@ -429,7 +385,7 @@ var ReactMount = { nextElement ); - var prevComponent = instancesByReactRootID[getReactRootID(container)]; + var prevComponent = getTopLevelWrapperInContainer(container); if (prevComponent) { var prevWrappedElement = prevComponent._currentElement; @@ -519,27 +475,6 @@ var ReactMount = { return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback); }, - /** - * Registers a container node into which React components will be rendered. - * This also creates the "reactRoot" ID that will be assigned to the element - * rendered within. - * - * @param {DOMElement} container DOM element to register as a container. - * @return {string} The "reactRoot" ID of elements rendered within. - */ - registerContainer: function(container) { - var id = getReactRootID(container); - // If one exists, make sure it is a valid "reactRoot" ID. - if (!id || id !== ReactInstanceHandles.getReactRootIDFromNodeID(id)) { - // No valid "reactRoot" ID found, create one. - id = ReactInstanceHandles.createReactRootID( - ClientReactRootIndex.createReactRootIndex() - ); - } - containersByReactRootID[id] = container; - return id; - }, - /** * Unmounts and destroys the React component rendered in the `container`. * @@ -571,19 +506,15 @@ var ReactMount = { 'unmountComponentAtNode(...): Target container is not a DOM element.' ); - var reactRootID = getReactRootID(container); - var component = instancesByReactRootID[reactRootID]; - if (!component) { + var prevComponent = getTopLevelWrapperInContainer(container); + if (!prevComponent) { // Check if the node being unmounted was rendered by React, but isn't a // root node. var containerHasNonRootReactChild = hasNonRootReactChild(container); // Check if the container itself is a React root node. - var containerID = internalGetID(container); var isContainerReactRoot = - containerID && - containerID === - ReactInstanceHandles.getReactRootIDFromNodeID(containerID); + container.nodeType === 1 && container.hasAttribute(ROOT_ATTR_NAME); if (__DEV__) { warning( @@ -604,14 +535,9 @@ var ReactMount = { } ReactUpdates.batchedUpdates( unmountComponentFromNode, - component, + prevComponent, container ); - delete instancesByReactRootID[reactRootID]; - delete containersByReactRootID[reactRootID]; - if (__DEV__) { - delete rootElementsByReactRootID[reactRootID]; - } return true; }, diff --git a/src/renderers/dom/client/__tests__/ReactRenderDocument-test.js b/src/renderers/dom/client/__tests__/ReactRenderDocument-test.js index 02230bf46b..6ba359b628 100644 --- a/src/renderers/dom/client/__tests__/ReactRenderDocument-test.js +++ b/src/renderers/dom/client/__tests__/ReactRenderDocument-test.js @@ -33,14 +33,6 @@ describe('rendering React components at document', function() { beforeEach(function() { jest.resetModuleRegistry(); - // Negative integer creator. So they won't get confused with - // the Client positive ids. - var ServerReactRootIndex = require('ServerReactRootIndex'); - var serverId = -1; - ServerReactRootIndex.createReactRootIndex.mockImplementation(function() { - return serverId--; - }); - React = require('React'); ReactDOM = require('ReactDOM'); ReactDOMServer = require('ReactDOMServer'); @@ -221,8 +213,8 @@ describe('rendering React components at document', function() { 'quirks by rendering at the document root. You should look for ' + 'environment dependent code in your components and ensure ' + 'the props are the same client and server side:\n' + - ' (client) ata-reactid=".-1.1">Hello worldGoodbye world' + ' (client) dy data-reactid="4">Hello worldGoodbye world' ); }); diff --git a/src/renderers/dom/server/ReactServerRendering.js b/src/renderers/dom/server/ReactServerRendering.js index ec543920c7..ffec7fcdf5 100644 --- a/src/renderers/dom/server/ReactServerRendering.js +++ b/src/renderers/dom/server/ReactServerRendering.js @@ -11,15 +11,14 @@ */ 'use strict'; +var ReactDOMContainerInfo = require('ReactDOMContainerInfo'); var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy'); var ReactElement = require('ReactElement'); -var ReactInstanceHandles = require('ReactInstanceHandles'); var ReactMarkupChecksum = require('ReactMarkupChecksum'); var ReactServerBatchingStrategy = require('ReactServerBatchingStrategy'); var ReactServerRenderingTransaction = require('ReactServerRenderingTransaction'); var ReactUpdates = require('ReactUpdates'); -var ServerReactRootIndex = require('ServerReactRootIndex'); var emptyObject = require('emptyObject'); var instantiateReactComponent = require('instantiateReactComponent'); @@ -39,18 +38,15 @@ function renderToStringImpl(element, makeStaticMarkup) { try { ReactUpdates.injection.injectBatchingStrategy(ReactServerBatchingStrategy); - var id = ReactInstanceHandles.createReactRootID( - ServerReactRootIndex.createReactRootIndex() - ); transaction = ReactServerRenderingTransaction.getPooled(makeStaticMarkup); return transaction.perform(function() { var componentInstance = instantiateReactComponent(element, null); var markup = componentInstance.mountComponent( - id, + '', transaction, null, - null, + ReactDOMContainerInfo(), emptyObject ); if (!makeStaticMarkup) { diff --git a/src/renderers/dom/server/__tests__/ReactServerRendering-test.js b/src/renderers/dom/server/__tests__/ReactServerRendering-test.js index 83c54a8741..2462d7e853 100644 --- a/src/renderers/dom/server/__tests__/ReactServerRendering-test.js +++ b/src/renderers/dom/server/__tests__/ReactServerRendering-test.js @@ -20,6 +20,7 @@ var ReactTestUtils; var ReactServerRendering; var ID_ATTRIBUTE_NAME; +var ROOT_ATTRIBUTE_NAME; describe('ReactServerRendering', function() { beforeEach(function() { @@ -36,7 +37,7 @@ describe('ReactServerRendering', function() { var DOMProperty = require('DOMProperty'); ID_ATTRIBUTE_NAME = DOMProperty.ID_ATTRIBUTE_NAME; - spyOn(console, 'error'); + ROOT_ATTRIBUTE_NAME = DOMProperty.ROOT_ATTRIBUTE_NAME; }); describe('renderToString', function() { @@ -45,7 +46,8 @@ describe('ReactServerRendering', function() { hello world ); expect(response).toMatch( - 'hello world' ); }); @@ -55,7 +57,8 @@ describe('ReactServerRendering', function() { ); expect(response).toMatch( - '' ); }); @@ -65,7 +68,8 @@ describe('ReactServerRendering', function() { ); expect(response).toMatch( - '' ); }); @@ -95,7 +99,8 @@ describe('ReactServerRendering', function() { ); expect(response).toMatch( - '
' + '' + 'My name is ' + @@ -145,7 +150,8 @@ describe('ReactServerRendering', function() { ); expect(response).toMatch( - '' + 'Component name: ' + 'TestComponent' + @@ -225,6 +231,7 @@ describe('ReactServerRendering', function() { // Now simulate a situation where the app is not idempotent. React should // warn but do the right thing. element.innerHTML = lastMarkup; + spyOn(console, 'error'); var instance = ReactDOM.render(, element); expect(mountCount).toEqual(4); expect(console.error.argsForCall.length).toBe(1); diff --git a/src/renderers/dom/shared/DOMProperty.js b/src/renderers/dom/shared/DOMProperty.js index 69c18dd56e..2da8d2d651 100644 --- a/src/renderers/dom/shared/DOMProperty.js +++ b/src/renderers/dom/shared/DOMProperty.js @@ -167,6 +167,7 @@ var defaultValueCache = {}; var DOMProperty = { ID_ATTRIBUTE_NAME: 'data-reactid', + ROOT_ATTRIBUTE_NAME: 'data-reactroot', /** * Map from property "standard name" to an object with info about how to set diff --git a/src/renderers/dom/shared/DOMPropertyOperations.js b/src/renderers/dom/shared/DOMPropertyOperations.js index 00aa1a5e56..092ea8af2b 100644 --- a/src/renderers/dom/shared/DOMPropertyOperations.js +++ b/src/renderers/dom/shared/DOMPropertyOperations.js @@ -125,6 +125,14 @@ var DOMPropertyOperations = { node.setAttribute(DOMProperty.ID_ATTRIBUTE_NAME, id); }, + createMarkupForRoot: function() { + return DOMProperty.ROOT_ATTRIBUTE_NAME + '=""'; + }, + + setAttributeForRoot: function(node) { + node.setAttribute(DOMProperty.ROOT_ATTRIBUTE_NAME, ''); + }, + /** * Creates markup for a property. * diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index 73b06e3c05..27ed5b6767 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -301,11 +301,11 @@ function enqueuePutListener(inst, registrationName, listener, transaction) { ); } var containerInfo = inst._nativeContainerInfo; - if (!containerInfo) { + var doc = containerInfo._ownerDocument; + if (!doc) { // Server rendering. return; } - var doc = containerInfo._ownerDocument; listenTo(registrationName, doc); transaction.getReactMountReady().enqueue(putListener, { inst: inst, @@ -494,6 +494,8 @@ function isCustomComponent(tagName, props) { return tagName.indexOf('-') >= 0 || props.is != null; } +var globalIdCounter = 1; + /** * Creates a new React class that is idempotent and capable of containing other * React components. It accepts event listeners and DOM properties that are @@ -518,6 +520,7 @@ function ReactDOMComponent(tag) { this._nativeNode = null; this._nativeParent = null; this._rootNodeID = null; + this._domID = null; this._nativeContainerInfo = null; this._wrapperState = null; this._topLevelWrapper = null; @@ -554,7 +557,8 @@ ReactDOMComponent.Mixin = { nativeContainerInfo, context ) { - this._rootNodeID = rootID; + this._rootNodeID = globalIdCounter++; + this._domID = nativeContainerInfo._idCounter++; this._nativeParent = nativeParent; this._nativeContainerInfo = nativeContainerInfo; @@ -604,7 +608,7 @@ ReactDOMComponent.Mixin = { if (nativeParent != null) { namespaceURI = nativeParent._namespaceURI; parentTag = nativeParent._tag; - } else if (nativeContainerInfo != null) { + } else if (nativeContainerInfo._tag) { namespaceURI = nativeContainerInfo._namespaceURI; parentTag = nativeContainerInfo._tag; } @@ -625,7 +629,7 @@ ReactDOMComponent.Mixin = { var parentInfo; if (nativeParent != null) { parentInfo = nativeParent._ancestorInfo; - } else if (nativeContainerInfo != null) { + } else if (nativeContainerInfo._tag) { parentInfo = nativeContainerInfo._ancestorInfo; } if (parentInfo) { @@ -660,7 +664,10 @@ ReactDOMComponent.Mixin = { } ReactDOMComponentTree.precacheNode(this, el); this._flags |= Flags.hasCachedChildNodes; - DOMPropertyOperations.setAttributeForID(el, this._rootNodeID); + if (!this._nativeParent) { + DOMPropertyOperations.setAttributeForRoot(el); + } + DOMPropertyOperations.setAttributeForID(el, this._domID); this._updateDOMProperties(null, props, transaction); var lazyTree = DOMLazyTree(el); this._createInitialChildren(transaction, props, context, lazyTree); @@ -757,8 +764,11 @@ ReactDOMComponent.Mixin = { return ret; } - var markupForID = DOMPropertyOperations.createMarkupForID(this._rootNodeID); - return ret + ' ' + markupForID; + if (!this._nativeParent) { + ret += ' ' + DOMPropertyOperations.createMarkupForRoot(); + } + ret += ' ' + DOMPropertyOperations.createMarkupForID(this._domID); + return ret; }, /** @@ -1145,6 +1155,7 @@ ReactDOMComponent.Mixin = { EventPluginHub.deleteAllListeners(this); ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID); this._rootNodeID = null; + this._domID = null; this._wrapperState = null; }, diff --git a/src/renderers/dom/shared/ReactDOMContainerInfo.js b/src/renderers/dom/shared/ReactDOMContainerInfo.js index cbee0b5e7d..f0b97c037f 100644 --- a/src/renderers/dom/shared/ReactDOMContainerInfo.js +++ b/src/renderers/dom/shared/ReactDOMContainerInfo.js @@ -15,15 +15,19 @@ var validateDOMNesting = require('validateDOMNesting'); var DOC_NODE_TYPE = 9; -function ReactDOMContainerInfo(node) { +function ReactDOMContainerInfo(topLevelWrapper, node) { var info = { - _ownerDocument: node.nodeType === DOC_NODE_TYPE ? node : node.ownerDocument, - _tag: node.nodeName.toLowerCase(), - _namespaceURI: node.namespaceURI, + _topLevelWrapper: topLevelWrapper, + _idCounter: 1, + _ownerDocument: node ? + node.nodeType === DOC_NODE_TYPE ? node : node.ownerDocument : + null, + _tag: node ? node.nodeName.toLowerCase() : null, + _namespaceURI: node ? node.namespaceURI : null, }; if (__DEV__) { - info._ancestorInfo = - validateDOMNesting.updatedAncestorInfo(null, info._tag, null); + info._ancestorInfo = node ? + validateDOMNesting.updatedAncestorInfo(null, info._tag, null) : null; } return info; } diff --git a/src/renderers/dom/shared/ReactDOMTextComponent.js b/src/renderers/dom/shared/ReactDOMTextComponent.js index 7404b61f37..4ffa9c4746 100644 --- a/src/renderers/dom/shared/ReactDOMTextComponent.js +++ b/src/renderers/dom/shared/ReactDOMTextComponent.js @@ -15,8 +15,6 @@ var DOMChildrenOperations = require('DOMChildrenOperations'); var DOMLazyTree = require('DOMLazyTree'); var DOMPropertyOperations = require('DOMPropertyOperations'); -var ReactComponentBrowserEnvironment = - require('ReactComponentBrowserEnvironment'); var ReactDOMComponentTree = require('ReactDOMComponentTree'); var ReactPerf = require('ReactPerf'); @@ -60,7 +58,7 @@ assign(ReactDOMTextComponent.prototype, { this._nativeParent = null; // Properties - this._rootNodeID = null; + this._domID = null; this._mountIndex = 0; }, @@ -94,13 +92,14 @@ assign(ReactDOMTextComponent.prototype, { } } - this._rootNodeID = rootID; + var domID = nativeContainerInfo._idCounter++; + this._domID = domID; this._nativeParent = nativeParent; if (transaction.useCreateElement) { var ownerDocument = nativeContainerInfo._ownerDocument; var el = ownerDocument.createElement('span'); ReactDOMComponentTree.precacheNode(this, el); - DOMPropertyOperations.setAttributeForID(el, rootID); + DOMPropertyOperations.setAttributeForID(el, domID); var lazyTree = DOMLazyTree(el); DOMLazyTree.queueText(lazyTree, this._stringText); return lazyTree; @@ -115,7 +114,7 @@ assign(ReactDOMTextComponent.prototype, { } return ( - '' + + '' + escapedText + '' ); @@ -148,8 +147,7 @@ assign(ReactDOMTextComponent.prototype, { }, unmountComponent: function() { - this._nativeNode = null; - ReactComponentBrowserEnvironment.unmountIDFromEnvironment(this._rootNodeID); + ReactDOMComponentTree.uncacheNode(this); }, }); diff --git a/src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js b/src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js index 049ecf0b0a..85f4cfa4f0 100644 --- a/src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js +++ b/src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js @@ -13,22 +13,11 @@ var React; var ReactDOM; -var ReactDOMServer; describe('ReactDOMTextComponent', function() { beforeEach(function() { React = require('React'); ReactDOM = require('ReactDOM'); - ReactDOMServer = require('ReactDOMServer'); - }); - - it('should escape the rootID', function() { - var ThisThingShouldBeEscaped = '">>> LULZ <<<"'; - var ThisThingWasBeEscaped = '">>> LULZ <<<"'; - var thing =
LULZ
; - var html = ReactDOMServer.renderToString(thing); - expect(html).not.toContain(ThisThingShouldBeEscaped); - expect(html).toContain(ThisThingWasBeEscaped); }); it('updates a mounted text component in place', function() { diff --git a/src/renderers/shared/reconciler/ReactMultiChild.js b/src/renderers/shared/reconciler/ReactMultiChild.js index bfb9bda1f4..1b0be8c563 100644 --- a/src/renderers/shared/reconciler/ReactMultiChild.js +++ b/src/renderers/shared/reconciler/ReactMultiChild.js @@ -249,11 +249,9 @@ var ReactMultiChild = { for (var name in children) { if (children.hasOwnProperty(name)) { var child = children[name]; - // Inlined for performance, see `ReactInstanceHandles.createReactID`. - var rootID = this._rootNodeID + name; var mountImage = ReactReconciler.mountComponent( child, - rootID, + name, transaction, this, this._nativeContainerInfo, @@ -510,11 +508,9 @@ var ReactMultiChild = { index, transaction, context) { - // Inlined for performance, see `ReactInstanceHandles.createReactID`. - var rootID = this._rootNodeID + name; var mountImage = ReactReconciler.mountComponent( child, - rootID, + name, transaction, this, this._nativeContainerInfo, diff --git a/src/renderers/shared/reconciler/__tests__/ReactIdentity-test.js b/src/renderers/shared/reconciler/__tests__/ReactIdentity-test.js index 2639ffb2d3..f5918b4abf 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactIdentity-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactIdentity-test.js @@ -13,7 +13,6 @@ var React; var ReactDOM; -var ReactDOMComponentTree; var ReactFragment; var ReactTestUtils; @@ -23,85 +22,52 @@ describe('ReactIdentity', function() { jest.resetModuleRegistry(); React = require('React'); ReactDOM = require('ReactDOM'); - ReactDOMComponentTree = require('ReactDOMComponentTree'); ReactFragment = require('ReactFragment'); ReactTestUtils = require('ReactTestUtils'); }); - var idExp = /^\.[^.]+(.*)$/; - function checkID(child, expectedID) { - // TODO: Node IDs are not public API; rewrite these tests. - var rootID = ReactDOMComponentTree.getInstanceFromNode(child)._rootNodeID; - var actual = idExp.exec(rootID); - var expected = idExp.exec(expectedID); - expect(actual).toBeTruthy(); - expect(expected).toBeTruthy(); - expect(actual[1]).toEqual(expected[1]); - } - function frag(obj) { return ReactFragment.create(obj); } - it('should allow keyed objects to express identity', function() { - var instance = -
- {frag({ - first:
, - second:
, - })} -
; - - instance = ReactDOM.render(instance, document.createElement('div')); - var node = ReactDOM.findDOMNode(instance); - expect(node.childNodes.length).toBe(2); - checkID(node.childNodes[0], '.0.$first/=10'); - checkID(node.childNodes[1], '.0.$second/=10'); - }); - it('should allow key property to express identity', function() { - var instance = -
-
-
-
-
+ var node; + var Component = (props) => +
node = c}> +
+
; - instance = ReactDOM.render(instance, document.createElement('div')); - var node = ReactDOM.findDOMNode(instance); - expect(node.childNodes.length).toBe(4); - checkID(node.childNodes[0], '.0.$apple'); - checkID(node.childNodes[1], '.0.$banana'); - checkID(node.childNodes[2], '.0.$0'); - checkID(node.childNodes[3], '.0.$123'); + var container = document.createElement('div'); + ReactDOM.render(, container); + var origChildren = Array.from(node.childNodes); + ReactDOM.render(, container); + var newChildren = Array.from(node.childNodes); + expect(origChildren[0]).toBe(newChildren[1]); + expect(origChildren[1]).toBe(newChildren[0]); }); - it('should use instance identity', function() { + it('should use composite identity', function() { var Wrapper = React.createClass({ render: function() { - return {this.props.children}; + return {this.props.children}; }, }); - var instance = -
- - - -
; + var container = document.createElement('div'); + var node1; + var node2; + ReactDOM.render( + node1 = c} />, + container + ); + ReactDOM.render( + node2 = c} />, + container + ); - instance = ReactDOM.render(instance, document.createElement('div')); - var node = ReactDOM.findDOMNode(instance); - expect(node.childNodes.length).toBe(3); - - checkID(node.childNodes[0], '.0.$wrap1'); - checkID(node.childNodes[0].firstChild, '.0.$wrap1.$squirrel'); - checkID(node.childNodes[1], '.0.$wrap2'); - checkID(node.childNodes[1].firstChild, '.0.$wrap2.$bunny'); - checkID(node.childNodes[2], '.0.2'); - checkID(node.childNodes[2].firstChild, '.0.2.$chipmunk'); + expect(node1).not.toBe(node2); }); function renderAComponentWithKeyIntoContainer(key, container) { @@ -125,11 +91,6 @@ describe('ReactIdentity', function() { expect(ReactDOM.findDOMNode(span1)).not.toBe(null); expect(ReactDOM.findDOMNode(span2)).not.toBe(null); - - key = key.replace(/=/g, '=0'); - checkID(ReactDOM.findDOMNode(span1), '.0.$' + key); - key = key.replace(/\//g, '//'); - checkID(ReactDOM.findDOMNode(span2), '.0.1:$' + key + '/=10'); } it('should allow any character as a key, in a detached parent', function() { diff --git a/src/renderers/shared/reconciler/__tests__/ReactMultiChildReconcile-test.js b/src/renderers/shared/reconciler/__tests__/ReactMultiChildReconcile-test.js index 5ca3e4afb1..012691a52c 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactMultiChildReconcile-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactMultiChildReconcile-test.js @@ -122,7 +122,7 @@ var FriendsStatusDisplay = React.createClass({ }); -function getInteralStateByUserName(statusDisplays) { +function getInternalStateByUserName(statusDisplays) { return mapObject(statusDisplays, function(statusDisplay, key) { return statusDisplay.getInternalState(); }); @@ -201,7 +201,9 @@ function verifyDomOrderingAccurate(parentInstance, statusDisplays) { continue; } var statusDisplay = statusDisplays[username]; - orderedLogicalIDs.push(ReactInstanceMap.get(statusDisplay)._rootNodeID); + orderedLogicalIDs.push( + ReactInstanceMap.get(statusDisplay)._renderedComponent._rootNodeID + ); } expect(orderedDomIDs).toEqual(orderedLogicalIDs); } @@ -217,7 +219,7 @@ function testPropsSequence(sequence) { container ); var statusDisplays = parentInstance.getStatusDisplays(); - var lastInternalStates = getInteralStateByUserName(statusDisplays); + var lastInternalStates = getInternalStateByUserName(statusDisplays); verifyStatuses(statusDisplays, sequence[0]); for (i = 1; i < sequence.length; i++) { @@ -230,7 +232,7 @@ function testPropsSequence(sequence) { verifyStatesPreserved(lastInternalStates, statusDisplays); verifyDomOrderingAccurate(parentInstance, statusDisplays); - lastInternalStates = getInteralStateByUserName(statusDisplays); + lastInternalStates = getInternalStateByUserName(statusDisplays); } }