diff --git a/src/core/ReactComponentBrowserEnvironment.js b/src/core/ReactComponentBrowserEnvironment.js index 519b91c80059..b8b3561a0d76 100644 --- a/src/core/ReactComponentBrowserEnvironment.js +++ b/src/core/ReactComponentBrowserEnvironment.js @@ -89,6 +89,7 @@ var ReactComponentBrowserEnvironment = { ); if (shouldReuseMarkup) { + console.log(markup); if (ReactMarkupChecksum.canReuseMarkup( markup, getReactRootElementInContainer(container))) { diff --git a/src/core/ReactMount.js b/src/core/ReactMount.js index 6f7c93d0e621..42a6f602a056 100644 --- a/src/core/ReactMount.js +++ b/src/core/ReactMount.js @@ -58,6 +58,43 @@ function getReactRootID(container) { return rootElement && ReactMount.getID(rootElement); } +var nextMountID = {}; +var mountIDtoReactID = {}; +var reactIDtoMountID = {}; + +function toBase62(value) { + var table = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + if (value < 62) { + return table.charAt(value); + } else { + var index = value % 62; + return toBase62((value - index) / 62) + table.charAt(index); + } +} + +function resetMountIDs() { + nextMountID = {}; + mountIDtoReactID = {}; + reactIDtoMountID = {}; +} + +function createMountID(id) { + var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(id).substr(1); + + nextMountID[rootID] = nextMountID[rootID] || 0; + + var mountID = rootID + ':' + toBase62(nextMountID[rootID]++); + mountIDtoReactID[mountID] = id; + reactIDtoMountID[id] = mountID; + return mountID; +} + +function purgeMountID(id) { + var mountID = reactIDtoMountID[id]; + //delete mountIDtoReactID[mountID]; + //delete reactIDtoMountID[id]; +} + /** * Accessing node[ATTR_NAME] or calling getAttribute(ATTR_NAME) on a form * element can return its control whose name or ID equals ATTR_NAME. All @@ -94,7 +131,11 @@ 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) || ''; + var id = node && node.getAttribute && node.getAttribute(ATTR_NAME); + if (id && !(id in mountIDtoReactID)) { + throw 'err ' + id; + } + return mountIDtoReactID[id] || ''; } /** @@ -106,9 +147,9 @@ function internalGetID(node) { function setID(node, id) { var oldID = internalGetID(node); if (oldID !== id) { - delete nodeCache[oldID]; + purgeID(oldID); } - node.setAttribute(ATTR_NAME, id); + node.setAttribute(ATTR_NAME, createMountID(id)); nodeCache[id] = node; } @@ -159,6 +200,7 @@ function isValid(node, id) { * @param {string} id The ID to forget. */ function purgeID(id) { + purgeMountID(id); delete nodeCache[id]; } @@ -617,6 +659,10 @@ var ReactMount = { * React ID utilities. */ + createMountID: createMountID, + + resetMountIDs: resetMountIDs, + getReactRootID: getReactRootID, getID: getID, diff --git a/src/core/__tests__/ReactDOMComponent-test.js b/src/core/__tests__/ReactDOMComponent-test.js index 68aa1d0fbada..5f729c6cd733 100644 --- a/src/core/__tests__/ReactDOMComponent-test.js +++ b/src/core/__tests__/ReactDOMComponent-test.js @@ -224,7 +224,7 @@ describe('ReactDOMComponent', function() { var NodeStub = function(initialProps) { this.props = initialProps || {}; - this._rootNodeID = 'test'; + this._rootNodeID = '.0'; }; mixInto(NodeStub, ReactDOMComponent.Mixin); diff --git a/src/core/__tests__/ReactEventEmitter-test.js b/src/core/__tests__/ReactEventEmitter-test.js index 0d23d855417b..a172804394c5 100644 --- a/src/core/__tests__/ReactEventEmitter-test.js +++ b/src/core/__tests__/ReactEventEmitter-test.js @@ -33,21 +33,17 @@ require('mock-modules') var keyOf = require('keyOf'); var mocks = require('mocks'); -var ReactMount = require('ReactMount'); var idToNode = {}; -var getID = ReactMount.getID; -var setID = function(el, id) { - ReactMount.setID(el, id); - idToNode[id] = el; -}; -var oldGetNode = ReactMount.getNode; +var ReactMount; var EventPluginHub; var ReactEventEmitter; var ReactTestUtils; var TapEventPlugin; var EventListener; +var getID; +var setID; var tapMoveThreshold; var idCallOrder = []; var recordID = function(id) { @@ -75,12 +71,9 @@ var ON_CHANGE_KEY = keyOf({onChange: null}); * feed them into `ReactEventEmitter` (through `ReactTestUtils`), the event * handlers may receive a DOM node to inspect. */ -var CHILD = document.createElement('div'); -var PARENT = document.createElement('div'); -var GRANDPARENT = document.createElement('div'); -setID(CHILD, '.reactRoot.[0].[0].[0]'); -setID(PARENT, '.reactRoot.[0].[0]'); -setID(GRANDPARENT, '.reactRoot.[0]'); +var CHILD; +var PARENT; +var GRANDPARENT; function registerSimpleTestHandler() { ReactEventEmitter.putListener(getID(CHILD), ON_CLICK_KEY, LISTENER); @@ -108,10 +101,17 @@ describe('ReactEventEmitter', function() { EventPluginHub.injection.injectEventPluginsByName({ TapEventPlugin: TapEventPlugin }); - }); - - afterEach(function() { - ReactMount.getNode = oldGetNode; + getID = ReactMount.getID; + setID = function(el, id) { + ReactMount.setID(el, id); + idToNode[id] = el; + }; + CHILD = document.createElement('div'); + PARENT = document.createElement('div'); + GRANDPARENT = document.createElement('div'); + setID(CHILD, '.reactRoot.[0].[0].[0]'); + setID(PARENT, '.reactRoot.[0].[0]'); + setID(GRANDPARENT, '.reactRoot.[0]'); }); it('should store a listener correctly', function() { diff --git a/src/dom/DOMPropertyOperations.js b/src/dom/DOMPropertyOperations.js index 595fe556d62b..ee3e0b169c8e 100644 --- a/src/dom/DOMPropertyOperations.js +++ b/src/dom/DOMPropertyOperations.js @@ -19,6 +19,7 @@ "use strict"; +var ReactMount = require('ReactMount'); var DOMProperty = require('DOMProperty'); var escapeTextForBrowser = require('escapeTextForBrowser'); @@ -79,7 +80,7 @@ var DOMPropertyOperations = { */ createMarkupForID: function(id) { return processAttributeNameAndPrefix(DOMProperty.ID_ATTRIBUTE_NAME) + - escapeTextForBrowser(id) + '"'; + escapeTextForBrowser(ReactMount.createMountID(id)) + '"'; }, /**