mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Kill ReactMount.getNode/getID/purgeID with fire
This commit is contained in:
@@ -28,7 +28,6 @@ var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var emptyObject = require('emptyObject');
|
||||
var containsNode = require('containsNode');
|
||||
var instantiateReactComponent = require('instantiateReactComponent');
|
||||
var invariant = require('invariant');
|
||||
var setInnerHTML = require('setInnerHTML');
|
||||
@@ -36,7 +35,6 @@ var shouldUpdateReactComponent = require('shouldUpdateReactComponent');
|
||||
var warning = require('warning');
|
||||
|
||||
var ATTR_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
|
||||
var nodeCache = {};
|
||||
|
||||
var ELEMENT_NODE_TYPE = 1;
|
||||
var DOC_NODE_TYPE = 9;
|
||||
@@ -54,9 +52,6 @@ if (__DEV__) {
|
||||
var rootElementsByReactRootID = {};
|
||||
}
|
||||
|
||||
// Used to store breadth-first search state in findComponentRoot.
|
||||
var findComponentRootReusableArray = [];
|
||||
|
||||
/**
|
||||
* Finds the index of the first character
|
||||
* that's not common between the two given strings.
|
||||
@@ -99,33 +94,6 @@ function getReactRootID(container) {
|
||||
return rootElement && internalGetID(rootElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* DOM nodes support `getAttributeNode` but this can also get called on
|
||||
* other objects so just return '' if we're given something other than a
|
||||
* DOM node (such as window).
|
||||
*
|
||||
* @param {?DOMElement|DOMWindow|DOMDocument|DOMTextNode} node DOM node.
|
||||
* @return {string} ID of the supplied `domNode`.
|
||||
*/
|
||||
function getID(node) {
|
||||
var id = internalGetID(node);
|
||||
if (id) {
|
||||
var cached = nodeCache[id];
|
||||
// TODO: Fix this whole concept of "validity" -- the cache just shouldn't
|
||||
// have nodes that have been unmounted.
|
||||
invariant(
|
||||
!cached || cached === node || !isValid(cached, id),
|
||||
'ReactMount: Two valid but unequal nodes with the same `%s`: %s',
|
||||
ATTR_NAME, id
|
||||
);
|
||||
nodeCache[id] = node;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -133,112 +101,6 @@ function internalGetID(node) {
|
||||
return node && node.getAttribute && node.getAttribute(ATTR_NAME) || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the React-specific ID of the given node.
|
||||
*
|
||||
* @param {DOMElement} node The DOM node whose ID will be set.
|
||||
* @param {string} id The value of the ID attribute.
|
||||
*/
|
||||
function setID(node, id) {
|
||||
var oldID = internalGetID(node);
|
||||
if (oldID !== id) {
|
||||
delete nodeCache[oldID];
|
||||
}
|
||||
node.setAttribute(ATTR_NAME, id);
|
||||
nodeCache[id] = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the node with the supplied ID if present in the cache.
|
||||
*/
|
||||
function getNodeIfCached(id) {
|
||||
var node = nodeCache[id];
|
||||
// TODO: Fix this whole concept of "validity" -- the cache just shouldn't have
|
||||
// nodes that have been unmounted.
|
||||
if (node && isValid(node, id)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the node with the supplied React-generated DOM ID.
|
||||
*
|
||||
* @param {string} id A React-generated DOM ID.
|
||||
* @return {DOMElement} DOM node with the suppled `id`.
|
||||
* @internal
|
||||
*/
|
||||
function getNode(id) {
|
||||
var node = getNodeIfCached(id);
|
||||
if (node) {
|
||||
return node;
|
||||
} else {
|
||||
return nodeCache[id] = ReactMount.findReactNodeByID(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node is "valid" if it is contained by a currently mounted container.
|
||||
*
|
||||
* This means that the node does not have to be contained by a document in
|
||||
* order to be considered valid.
|
||||
*
|
||||
* @param {?DOMElement} node The candidate DOM node.
|
||||
* @param {string} id The expected ID of the node.
|
||||
* @return {boolean} Whether the node is contained by a mounted container.
|
||||
*/
|
||||
function isValid(node, id) {
|
||||
if (node) {
|
||||
invariant(
|
||||
internalGetID(node) === id,
|
||||
'ReactMount: Unexpected modification of `%s`',
|
||||
ATTR_NAME
|
||||
);
|
||||
|
||||
var container = ReactMount.findReactContainerForID(id);
|
||||
if (container && containsNode(container, node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the cache to forget about one React-specific ID.
|
||||
*
|
||||
* @param {string} id The ID to forget.
|
||||
*/
|
||||
function purgeID(id) {
|
||||
delete nodeCache[id];
|
||||
}
|
||||
|
||||
var deepestNodeSoFar = null;
|
||||
function findDeepestCachedAncestorImpl(ancestorID) {
|
||||
var ancestor = getNodeIfCached(ancestorID);
|
||||
if (ancestor) {
|
||||
deepestNodeSoFar = ancestor;
|
||||
} else {
|
||||
// This node isn't populated in the cache, so presumably none of its
|
||||
// descendants are. Break out of the loop.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the deepest cached node whose ID is a prefix of `targetID`.
|
||||
*/
|
||||
function findDeepestCachedAncestor(targetID) {
|
||||
deepestNodeSoFar = null;
|
||||
ReactInstanceHandles.traverseAncestors(
|
||||
targetID,
|
||||
findDeepestCachedAncestorImpl
|
||||
);
|
||||
|
||||
var foundNode = deepestNodeSoFar;
|
||||
deepestNodeSoFar = null;
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts this component and inserts it into the DOM.
|
||||
*
|
||||
@@ -344,47 +206,6 @@ function hasNonRootReactChild(node) {
|
||||
ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first (deepest) ancestor of a node which is rendered by this copy
|
||||
* of React.
|
||||
*/
|
||||
function findFirstReactDOMImpl(node) {
|
||||
// This node might be from another React instance, so we make sure not to
|
||||
// examine the node cache here
|
||||
for (; node && node.parentNode !== node; node = node.parentNode) {
|
||||
if (node.nodeType !== 1) {
|
||||
// Not a DOMElement, therefore not a React component
|
||||
continue;
|
||||
}
|
||||
var nodeID = internalGetID(node);
|
||||
if (!nodeID) {
|
||||
continue;
|
||||
}
|
||||
var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID);
|
||||
|
||||
// If containersByReactRootID contains the container we find by crawling up
|
||||
// the tree, we know that this instance of React rendered the node.
|
||||
// nb. isValid's strategy (with containsNode) does not work because render
|
||||
// trees may be nested and we don't want a false positive in that case.
|
||||
var current = node;
|
||||
var lastID;
|
||||
do {
|
||||
lastID = internalGetID(current);
|
||||
current = current.parentNode;
|
||||
if (current == null) {
|
||||
// The passed-in node has been detached from the container it was
|
||||
// originally rendered into.
|
||||
return null;
|
||||
}
|
||||
} while (lastID !== reactRootID);
|
||||
|
||||
if (current === containersByReactRootID[reactRootID]) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporary (?) hack so that we can store all top-level pending updates on
|
||||
* composites instead of having to worry about different types of components
|
||||
@@ -794,162 +615,6 @@ var ReactMount = {
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds the container DOM element that contains React component to which the
|
||||
* supplied DOM `id` belongs.
|
||||
*
|
||||
* @param {string} id The ID of an element rendered by a React component.
|
||||
* @return {?DOMElement} DOM element that contains the `id`.
|
||||
*/
|
||||
findReactContainerForID: function(id) {
|
||||
var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id);
|
||||
var container = containersByReactRootID[reactRootID];
|
||||
|
||||
if (__DEV__) {
|
||||
var rootElement = rootElementsByReactRootID[reactRootID];
|
||||
if (rootElement && rootElement.parentNode !== container) {
|
||||
warning(
|
||||
// Call internalGetID here because getID calls isValid which calls
|
||||
// findReactContainerForID (this function).
|
||||
internalGetID(rootElement) === reactRootID,
|
||||
'ReactMount: Root element ID differed from reactRootID.'
|
||||
);
|
||||
var containerChild = container.firstChild;
|
||||
if (containerChild &&
|
||||
reactRootID === internalGetID(containerChild)) {
|
||||
// If the container has a new child with the same ID as the old
|
||||
// root element, then rootElementsByReactRootID[reactRootID] is
|
||||
// just stale and needs to be updated. The case that deserves a
|
||||
// warning is when the container is empty.
|
||||
rootElementsByReactRootID[reactRootID] = containerChild;
|
||||
} else {
|
||||
warning(
|
||||
false,
|
||||
'ReactMount: Root element has been removed from its original ' +
|
||||
'container. New container: %s',
|
||||
rootElement.parentNode
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds an element rendered by React with the supplied ID.
|
||||
*
|
||||
* @param {string} id ID of a DOM node in the React component.
|
||||
* @return {DOMElement} Root DOM node of the React component.
|
||||
*/
|
||||
findReactNodeByID: function(id) {
|
||||
var reactRoot = ReactMount.findReactContainerForID(id);
|
||||
return ReactMount.findComponentRoot(reactRoot, id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Traverses up the ancestors of the supplied node to find a node that is a
|
||||
* DOM representation of a React component rendered by this copy of React.
|
||||
*
|
||||
* @param {*} node
|
||||
* @return {?DOMEventTarget}
|
||||
* @internal
|
||||
*/
|
||||
getFirstReactDOM: function(node) {
|
||||
return findFirstReactDOMImpl(node);
|
||||
},
|
||||
|
||||
/**
|
||||
* Finds a node with the supplied `targetID` inside of the supplied
|
||||
* `ancestorNode`. Exploits the ID naming scheme to perform the search
|
||||
* quickly.
|
||||
*
|
||||
* @param {DOMEventTarget} ancestorNode Search from this root.
|
||||
* @pararm {string} targetID ID of the DOM representation of the component.
|
||||
* @return {DOMEventTarget} DOM node with the supplied `targetID`.
|
||||
* @internal
|
||||
*/
|
||||
findComponentRoot: function(ancestorNode, targetID) {
|
||||
var firstChildren = findComponentRootReusableArray;
|
||||
var childIndex = 0;
|
||||
|
||||
var deepestAncestor = findDeepestCachedAncestor(targetID) || ancestorNode;
|
||||
|
||||
if (__DEV__) {
|
||||
// This will throw on the next line; give an early warning
|
||||
warning(
|
||||
deepestAncestor != null,
|
||||
'React can\'t find the root component node for data-reactid value ' +
|
||||
'`%s`. If you\'re seeing this message, it probably means that ' +
|
||||
'you\'ve loaded two copies of React on the page. At this time, only ' +
|
||||
'a single copy of React can be loaded at a time.',
|
||||
targetID
|
||||
);
|
||||
}
|
||||
|
||||
firstChildren[0] = deepestAncestor.firstChild;
|
||||
firstChildren.length = 1;
|
||||
|
||||
while (childIndex < firstChildren.length) {
|
||||
var child = firstChildren[childIndex++];
|
||||
var targetChild;
|
||||
|
||||
while (child) {
|
||||
var childID = ReactMount.getID(child);
|
||||
if (childID) {
|
||||
// Even if we find the node we're looking for, we finish looping
|
||||
// through its siblings to ensure they're cached so that we don't have
|
||||
// to revisit this node again. Otherwise, we make n^2 calls to getID
|
||||
// when visiting the many children of a single node in order.
|
||||
|
||||
if (targetID === childID) {
|
||||
targetChild = child;
|
||||
} else if (ReactInstanceHandles.isAncestorIDOf(childID, targetID)) {
|
||||
// If we find a child whose ID is an ancestor of the given ID,
|
||||
// then we can be sure that we only want to search the subtree
|
||||
// rooted at this child, so we can throw out the rest of the
|
||||
// search state.
|
||||
firstChildren.length = childIndex = 0;
|
||||
firstChildren.push(child.firstChild);
|
||||
}
|
||||
|
||||
} else {
|
||||
// If this child had no ID, then there's a chance that it was
|
||||
// injected automatically by the browser, as when a `<table>`
|
||||
// element sprouts an extra `<tbody>` child as a side effect of
|
||||
// `.innerHTML` parsing. Optimistically continue down this
|
||||
// branch, but not before examining the other siblings.
|
||||
firstChildren.push(child.firstChild);
|
||||
}
|
||||
|
||||
child = child.nextSibling;
|
||||
}
|
||||
|
||||
if (targetChild) {
|
||||
// Emptying firstChildren/findComponentRootReusableArray is
|
||||
// not necessary for correctness, but it helps the GC reclaim
|
||||
// any nodes that were left at the end of the search.
|
||||
firstChildren.length = 0;
|
||||
|
||||
return targetChild;
|
||||
}
|
||||
}
|
||||
|
||||
firstChildren.length = 0;
|
||||
|
||||
invariant(
|
||||
false,
|
||||
'findComponentRoot(..., %s): Unable to find element. This probably ' +
|
||||
'means the DOM was unexpectedly mutated (e.g., by the browser), ' +
|
||||
'usually due to forgetting a <tbody> when using tables, nesting tags ' +
|
||||
'like <form>, <p>, or <a>, or using non-SVG elements in an <svg> ' +
|
||||
'parent. ' +
|
||||
'Try inspecting the child nodes of the element with React ID `%s`.',
|
||||
targetID,
|
||||
ReactMount.getID(ancestorNode)
|
||||
);
|
||||
},
|
||||
|
||||
_mountImageIntoNode: function(
|
||||
markup,
|
||||
container,
|
||||
@@ -1056,22 +721,6 @@ var ReactMount = {
|
||||
ReactDOMComponentTree.precacheNode(instance, container.firstChild);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* React ID utilities.
|
||||
*/
|
||||
|
||||
getReactRootID: getReactRootID,
|
||||
|
||||
getID: getID,
|
||||
|
||||
setID: setID,
|
||||
|
||||
getNode: getNode,
|
||||
|
||||
isValid: isValid,
|
||||
|
||||
purgeID: purgeID,
|
||||
};
|
||||
|
||||
ReactPerf.measureMethods(ReactMount, 'ReactMount', {
|
||||
|
||||
@@ -218,61 +218,6 @@ describe('ReactMount', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not crash in node cache when unmounting', function() {
|
||||
var Component = React.createClass({
|
||||
render: function() {
|
||||
// Add refs to some nodes so that they get traversed and cached
|
||||
return (
|
||||
<div ref="a">
|
||||
<div ref="b">b</div>
|
||||
{this.props.showC && <div>c</div>}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var container = document.createElement('container');
|
||||
|
||||
ReactDOM.render(<div><Component showC={false} /></div>, container);
|
||||
|
||||
// Right now, A and B are in the cache. When we add C, it won't get added to
|
||||
// the cache (assuming markup-string mode).
|
||||
ReactDOM.render(<div><Component showC={true} /></div>, container);
|
||||
|
||||
// Remove A, B, and C. Unmounting C shouldn't cause B to get recached.
|
||||
ReactDOM.render(<div></div>, container);
|
||||
|
||||
// Add them back -- this shouldn't cause a cached node collision.
|
||||
ReactDOM.render(<div><Component showC={true} /></div>, container);
|
||||
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
});
|
||||
|
||||
it('should not crash in node cache when unmounting, case 2', function() {
|
||||
var A = React.createClass({
|
||||
render: function() {
|
||||
return <a key={this.props.innerKey}>{this.props.innerKey}</a>;
|
||||
},
|
||||
});
|
||||
var Component = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<b>
|
||||
<i>{this.props.step === 1 && <q />}</i>
|
||||
{this.props.step === 1 && <A innerKey={this.props.step} />}
|
||||
</b>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var container = document.createElement('container');
|
||||
|
||||
ReactDOM.render(<Component step={1} />, container);
|
||||
ReactDOM.render(<Component step={2} />, container);
|
||||
ReactDOM.render(<Component step={1} />, container);
|
||||
ReactMount.getID(container.querySelector('a'));
|
||||
});
|
||||
|
||||
it('passes the correct callback context', function() {
|
||||
var container = document.createElement('div');
|
||||
var calls = 0;
|
||||
|
||||
@@ -17,8 +17,6 @@ jest
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMServer;
|
||||
var ReactInstanceMap;
|
||||
var ReactMount;
|
||||
|
||||
var getTestDocument;
|
||||
|
||||
@@ -46,14 +44,12 @@ describe('rendering React components at document', function() {
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMServer = require('ReactDOMServer');
|
||||
ReactInstanceMap = require('ReactInstanceMap');
|
||||
ReactMount = require('ReactMount');
|
||||
getTestDocument = require('getTestDocument');
|
||||
|
||||
testDocument = getTestDocument();
|
||||
});
|
||||
|
||||
it('should be able to get root component id for document node', function() {
|
||||
it('should be able to adopt server markup', function() {
|
||||
expect(testDocument).not.toBeUndefined();
|
||||
|
||||
var Root = React.createClass({
|
||||
@@ -64,22 +60,24 @@ describe('rendering React components at document', function() {
|
||||
<title>Hello World</title>
|
||||
</head>
|
||||
<body>
|
||||
Hello world
|
||||
{'Hello ' + this.props.hello}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var markup = ReactDOMServer.renderToString(<Root />);
|
||||
var markup = ReactDOMServer.renderToString(<Root hello="world" />);
|
||||
testDocument = getTestDocument(markup);
|
||||
var component = ReactDOM.render(<Root />, testDocument);
|
||||
var body = testDocument.body;
|
||||
|
||||
ReactDOM.render(<Root hello="world" />, testDocument);
|
||||
expect(testDocument.body.innerHTML).toBe('Hello world');
|
||||
|
||||
// TODO: This is a bad test. I have no idea what this is testing.
|
||||
// Node IDs is an implementation detail and not part of the public API.
|
||||
var componentID = ReactMount.getReactRootID(testDocument);
|
||||
expect(componentID).toBe(ReactInstanceMap.get(component)._rootNodeID);
|
||||
ReactDOM.render(<Root hello="moon" />, testDocument);
|
||||
expect(testDocument.body.innerHTML).toBe('Hello moon');
|
||||
|
||||
expect(body).toBe(testDocument.body);
|
||||
});
|
||||
|
||||
it('should not be able to unmount component from document node', function() {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var LinkedValueUtils = require('LinkedValueUtils');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactUpdates = require('ReactUpdates');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
@@ -189,18 +188,12 @@ function _handleChange(event) {
|
||||
// This will throw if radio buttons rendered by different copies of React
|
||||
// and the same name are rendered into the same form (same as #1939).
|
||||
// That's probably okay; we don't support it just as we don't support
|
||||
// mixing React with non-React.
|
||||
var otherID = ReactMount.getID(otherNode);
|
||||
invariant(
|
||||
otherID,
|
||||
'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
|
||||
'same `name` is not supported.'
|
||||
);
|
||||
var otherInstance = instancesByReactID[otherID];
|
||||
// mixing React radio buttons with non-React ones.
|
||||
var otherInstance = ReactDOMComponentTree.getInstanceFromNode(otherNode);
|
||||
invariant(
|
||||
otherInstance,
|
||||
'ReactDOMInput: Unknown radio button ID %s.',
|
||||
otherID
|
||||
'ReactDOMInput: Mixing React and non-React radio inputs with the ' +
|
||||
'same `name` is not supported.'
|
||||
);
|
||||
// If this is a controlled radio button group, forcing the input that
|
||||
// was previously checked to update will cause it to be come re-checked
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
var DOMChildrenOperations = require('DOMChildrenOperations');
|
||||
var ReactDOMIDOperations = require('ReactDOMIDOperations');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
|
||||
/**
|
||||
@@ -37,7 +36,6 @@ var ReactComponentBrowserEnvironment = {
|
||||
* @private
|
||||
*/
|
||||
unmountIDFromEnvironment: function(rootNodeID) {
|
||||
ReactMount.purgeID(rootNodeID);
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
@@ -33,7 +33,6 @@ var ReactDOMInput = require('ReactDOMInput');
|
||||
var ReactDOMOption = require('ReactDOMOption');
|
||||
var ReactDOMSelect = require('ReactDOMSelect');
|
||||
var ReactDOMTextarea = require('ReactDOMTextarea');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactMultiChild = require('ReactMultiChild');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
var ReactUpdateQueue = require('ReactUpdateQueue');
|
||||
@@ -662,8 +661,6 @@ ReactDOMComponent.Mixin = {
|
||||
ReactDOMComponentTree.precacheNode(this, el);
|
||||
this._flags |= Flags.hasCachedChildNodes;
|
||||
DOMPropertyOperations.setAttributeForID(el, this._rootNodeID);
|
||||
// Populate node cache
|
||||
ReactMount.getID(el);
|
||||
this._updateDOMProperties(null, props, transaction);
|
||||
var lazyTree = DOMLazyTree(el);
|
||||
this._createInitialChildren(transaction, props, context, lazyTree);
|
||||
|
||||
@@ -18,7 +18,6 @@ var DOMPropertyOperations = require('DOMPropertyOperations');
|
||||
var ReactComponentBrowserEnvironment =
|
||||
require('ReactComponentBrowserEnvironment');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactMount = require('ReactMount');
|
||||
|
||||
var assign = require('Object.assign');
|
||||
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
|
||||
@@ -99,10 +98,8 @@ assign(ReactDOMTextComponent.prototype, {
|
||||
if (transaction.useCreateElement) {
|
||||
var ownerDocument = nativeContainerInfo._ownerDocument;
|
||||
var el = ownerDocument.createElement('span');
|
||||
this._nativeNode = el;
|
||||
ReactDOMComponentTree.precacheNode(this, el);
|
||||
DOMPropertyOperations.setAttributeForID(el, rootID);
|
||||
// Populate node cache
|
||||
ReactMount.getID(el);
|
||||
var lazyTree = DOMLazyTree(el);
|
||||
DOMLazyTree.queueText(lazyTree, this._stringText);
|
||||
return lazyTree;
|
||||
|
||||
@@ -16,7 +16,6 @@ var MorphingComponent;
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactCurrentOwner;
|
||||
var ReactMount;
|
||||
var ReactPropTypes;
|
||||
var ReactServerRendering;
|
||||
var ReactTestUtils;
|
||||
@@ -34,7 +33,6 @@ describe('ReactCompositeComponent', function() {
|
||||
ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
ReactPropTypes = require('ReactPropTypes');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
ReactMount = require('ReactMount');
|
||||
ReactServerRendering = require('ReactServerRendering');
|
||||
ReactUpdates = require('ReactUpdates');
|
||||
|
||||
@@ -487,8 +485,6 @@ describe('ReactCompositeComponent', function() {
|
||||
var container = document.createElement('div');
|
||||
var innerUnmounted = false;
|
||||
|
||||
spyOn(ReactMount, 'purgeID').andCallThrough();
|
||||
|
||||
var Component = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
@@ -501,11 +497,6 @@ describe('ReactCompositeComponent', function() {
|
||||
});
|
||||
var Inner = React.createClass({
|
||||
componentWillUnmount: function() {
|
||||
// It's important that ReactMount.purgeID is called after any component
|
||||
// lifecycle methods, because a componentWillUnmount implementation is
|
||||
// likely to call ReactDOM.findDOMNode(this), which will repopulate the
|
||||
// node cache after it's been cleared, causing a memory leak.
|
||||
expect(ReactMount.purgeID.calls.length).toBe(0);
|
||||
innerUnmounted = true;
|
||||
},
|
||||
render: function() {
|
||||
@@ -516,12 +507,6 @@ describe('ReactCompositeComponent', function() {
|
||||
ReactDOM.render(<Component />, container);
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
expect(innerUnmounted).toBe(true);
|
||||
|
||||
// The text and both <div /> elements and their wrappers each call
|
||||
// unmountIDFromEnvironment which calls purgeID, for a total of 3.
|
||||
// TODO: Test the effect of this. E.g. does the node cache get repopulated
|
||||
// after a getDOMNode call?
|
||||
expect(ReactMount.purgeID.calls.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should warn when shouldComponentUpdate() returns undefined', function() {
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMComponentTree;
|
||||
var ReactFragment;
|
||||
var ReactTestUtils;
|
||||
var ReactMount;
|
||||
|
||||
describe('ReactIdentity', function() {
|
||||
|
||||
@@ -23,14 +23,16 @@ describe('ReactIdentity', function() {
|
||||
jest.resetModuleRegistry();
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
ReactFragment = require('ReactFragment');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
ReactMount = require('ReactMount');
|
||||
});
|
||||
|
||||
var idExp = /^\.[^.]+(.*)$/;
|
||||
function checkID(child, expectedID) {
|
||||
var actual = idExp.exec(ReactMount.getID(child));
|
||||
// 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();
|
||||
@@ -297,14 +299,16 @@ describe('ReactIdentity', function() {
|
||||
var wrapped = <TestContainer first={instance0} second={instance1} />;
|
||||
|
||||
wrapped = ReactDOM.render(wrapped, document.createElement('div'));
|
||||
var div = ReactDOM.findDOMNode(wrapped);
|
||||
|
||||
var beforeID = ReactMount.getID(ReactDOM.findDOMNode(wrapped).firstChild);
|
||||
|
||||
var beforeA = div.childNodes[0];
|
||||
var beforeB = div.childNodes[1];
|
||||
wrapped.swap();
|
||||
var afterA = div.childNodes[1];
|
||||
var afterB = div.childNodes[0];
|
||||
|
||||
var afterID = ReactMount.getID(ReactDOM.findDOMNode(wrapped).firstChild);
|
||||
|
||||
expect(beforeID).not.toEqual(afterID);
|
||||
expect(beforeA).toBe(afterA);
|
||||
expect(beforeB).toBe(afterB);
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
var React = require('React');
|
||||
var ReactTestUtils = require('ReactTestUtils');
|
||||
var ReactMount = require('ReactMount');
|
||||
|
||||
/**
|
||||
* Ensure that all callbacks are invoked, passing this unique argument.
|
||||
@@ -74,89 +73,6 @@ describe('ReactInstanceHandles', function() {
|
||||
aggregatedArgs = [];
|
||||
});
|
||||
|
||||
describe('findComponentRoot', function() {
|
||||
it('should find the correct node with prefix sibling IDs', function() {
|
||||
var parentNode = ReactTestUtils.renderIntoDocument(
|
||||
<div>
|
||||
<div />
|
||||
{[<div key="x" />]}
|
||||
</div>
|
||||
);
|
||||
var childNodeB = parentNode.childNodes[1];
|
||||
|
||||
expect(
|
||||
ReactMount.getNode(
|
||||
getNodeID(childNodeB)
|
||||
)
|
||||
).toBe(childNodeB);
|
||||
});
|
||||
|
||||
it('should work around unidentified nodes', function() {
|
||||
var parentNode = ReactTestUtils.renderIntoDocument(
|
||||
<div>
|
||||
{[<div key="x" />]}
|
||||
</div>
|
||||
);
|
||||
var childNodeB = parentNode.childNodes[0];
|
||||
|
||||
// No ID on `childNodeA`.
|
||||
var childNodeA = document.createElement('div');
|
||||
parentNode.insertBefore(childNodeA, childNodeB);
|
||||
|
||||
expect(
|
||||
ReactMount.getNode(
|
||||
getNodeID(childNodeB)
|
||||
)
|
||||
).toBe(childNodeB);
|
||||
});
|
||||
|
||||
it('should throw if a rendered element cannot be found', function() {
|
||||
spyOn(console, 'error');
|
||||
var parentNode = ReactTestUtils.renderIntoDocument(
|
||||
<table>
|
||||
<tr />
|
||||
</table>
|
||||
);
|
||||
var childNodeA = parentNode.childNodes[0];
|
||||
var childNodeB;
|
||||
if (childNodeA.tagName === 'TR') {
|
||||
childNodeB = childNodeA;
|
||||
// No ID on `childNodeA`, it was "rendered by the browser".
|
||||
childNodeA = document.createElement('tbody');
|
||||
childNodeA.appendChild(childNodeB);
|
||||
parentNode.appendChild(childNodeA);
|
||||
} else {
|
||||
childNodeB = childNodeA.childNodes[0];
|
||||
}
|
||||
expect(childNodeA.tagName).toBe('TBODY');
|
||||
|
||||
expect(
|
||||
ReactMount.getNode(
|
||||
getNodeID(childNodeB)
|
||||
)
|
||||
).toBe(childNodeB);
|
||||
|
||||
var junkID = getNodeID(childNodeB) + ':junk';
|
||||
expect(function() {
|
||||
ReactMount.getNode(
|
||||
junkID
|
||||
);
|
||||
}).toThrow(
|
||||
'findComponentRoot(..., ' + junkID + '): Unable to find element. ' +
|
||||
'This probably means the DOM was unexpectedly mutated (e.g., by the ' +
|
||||
'browser), usually due to forgetting a <tbody> when using tables, ' +
|
||||
'nesting tags like <form>, <p>, or <a>, or using non-SVG elements in ' +
|
||||
'an <svg> parent. Try inspecting the child nodes of the element with ' +
|
||||
'React ID ``.'
|
||||
);
|
||||
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'<tr> cannot appear as a child of <table>'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getReactRootIDFromNodeID', function() {
|
||||
it('should support strings', function() {
|
||||
var test = '.s_0_1.0..1';
|
||||
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
var React = require('React');
|
||||
var ReactDOM = require('ReactDOM');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactInstanceMap = require('ReactInstanceMap');
|
||||
var ReactMount = require('ReactMount');
|
||||
|
||||
var mapObject = require('mapObject');
|
||||
|
||||
@@ -190,7 +190,8 @@ function verifyDomOrderingAccurate(parentInstance, statusDisplays) {
|
||||
var i;
|
||||
var orderedDomIDs = [];
|
||||
for (i = 0; i < statusDisplayNodes.length; i++) {
|
||||
orderedDomIDs.push(ReactMount.getID(statusDisplayNodes[i]));
|
||||
var inst = ReactDOMComponentTree.getInstanceFromNode(statusDisplayNodes[i]);
|
||||
orderedDomIDs.push(inst._rootNodeID);
|
||||
}
|
||||
|
||||
var orderedLogicalIDs = [];
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
'use strict';
|
||||
|
||||
var DOMProperty = require('DOMProperty');
|
||||
var ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
var ReactDefaultPerfAnalysis = require('ReactDefaultPerfAnalysis');
|
||||
var ReactMount = require('ReactMount');
|
||||
var ReactPerf = require('ReactPerf');
|
||||
@@ -175,8 +176,7 @@ var ReactDefaultPerf = {
|
||||
totalTime = performanceNow() - start;
|
||||
|
||||
if (fnName === '_mountImageIntoNode') {
|
||||
var mountID = ReactMount.getID(args[1]);
|
||||
ReactDefaultPerf._recordWrite(mountID, fnName, totalTime, args[0]);
|
||||
ReactDefaultPerf._recordWrite('', fnName, totalTime, args[0]);
|
||||
} else if (fnName === 'dangerouslyProcessChildrenUpdates') {
|
||||
// special format
|
||||
args[0].forEach(function(update) {
|
||||
@@ -206,7 +206,7 @@ var ReactDefaultPerf = {
|
||||
if (moduleName === 'EventPluginHub') {
|
||||
id = id._rootNodeID;
|
||||
} else if (typeof id === 'object') {
|
||||
id = ReactMount.getID(args[0]);
|
||||
id = ReactDOMComponentTree.getInstanceFromNode(args[0])._rootNodeID;
|
||||
}
|
||||
ReactDefaultPerf._recordWrite(
|
||||
id,
|
||||
|
||||
Reference in New Issue
Block a user