mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Throw on Missing Elements
This changes React to throw when `ReactID.getNode()` fails to find a node. This method is used by two call sites: - Implements `ReactComponent#getDOMNode`. This method already throws if a component is not mounted, and //all mounted components should be able to find their rendered root nodes//. - Used by `ReactDOMIDOperations`. These call sites aleady assume that `getNode` returns a non-null. Currently, if the node is not found, this is the site that fatals (and the stack trace is much harder to debug). The error message should make it //a lot// easier to debug unexpected DOM trees. In particular, this will help track down all the places where the browser inserts `<tbody>` unexpectedly.
This commit is contained in:
committed by
Paul O’Shannessy
parent
c54900f63e
commit
6556881417
@@ -276,7 +276,7 @@ var ReactComponent = {
|
||||
/**
|
||||
* Returns the DOM node rendered by this component.
|
||||
*
|
||||
* @return {?DOMElement} The root node of this component.
|
||||
* @return {DOMElement} The root node of this component.
|
||||
* @final
|
||||
* @protected
|
||||
*/
|
||||
|
||||
+4
-9
@@ -85,19 +85,14 @@ function setID(node, id) {
|
||||
* 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`.
|
||||
* @return {DOMElement} DOM node with the suppled `id`.
|
||||
* @internal
|
||||
*/
|
||||
function getNode(id) {
|
||||
if (nodeCache.hasOwnProperty(id)) {
|
||||
var node = nodeCache[id];
|
||||
if (isValid(node, id)) {
|
||||
return node;
|
||||
}
|
||||
if (!nodeCache.hasOwnProperty(id) || !isValid(nodeCache[id], id)) {
|
||||
nodeCache[id] = ReactMount.findReactNodeByID(id);
|
||||
}
|
||||
|
||||
return nodeCache[id] =
|
||||
ReactMount.findReactRenderedDOMNodeSlow(id);
|
||||
return nodeCache[id];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -280,21 +280,31 @@ var ReactInstanceHandles = {
|
||||
*
|
||||
* @param {DOMEventTarget} ancestorNode Search from this root.
|
||||
* @pararm {string} id ID of the DOM representation of the component.
|
||||
* @return {?DOMEventTarget} DOM node with the supplied `id`, if one exists.
|
||||
* @return {DOMEventTarget} DOM node with the supplied `id`.
|
||||
* @internal
|
||||
*/
|
||||
findComponentRoot: function(ancestorNode, id) {
|
||||
var child = ancestorNode.firstChild;
|
||||
while (child) {
|
||||
var childID = ReactID.getID(child);
|
||||
if (id === childID) {
|
||||
return child;
|
||||
} else if (childID && isAncestorIDOf(childID, id)) {
|
||||
return ReactInstanceHandles.findComponentRoot(child, id);
|
||||
if (childID) {
|
||||
if (id === childID) {
|
||||
return child;
|
||||
} else if (isAncestorIDOf(childID, id)) {
|
||||
return ReactInstanceHandles.findComponentRoot(child, id);
|
||||
}
|
||||
}
|
||||
child = child.nextSibling;
|
||||
}
|
||||
// Effectively: return null;
|
||||
invariant(
|
||||
false,
|
||||
'findComponentRoot: Unable to find element by React ID, `%s`. This ' +
|
||||
'indicates that someone (or the browser) has mutated the DOM tree in ' +
|
||||
'an unexpected way. Try inspecting the child nodes of the element with ' +
|
||||
'React ID, `%s`.',
|
||||
id,
|
||||
ReactID.getID(ancestorNode)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -269,13 +269,12 @@ var ReactMount = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Given the ID of a DOM node rendered by a React component, finds the root
|
||||
* DOM node of the React component.
|
||||
* 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.
|
||||
* @return {DOMElement} Root DOM node of the React component.
|
||||
*/
|
||||
findReactRenderedDOMNodeSlow: function(id) {
|
||||
findReactNodeByID: function(id) {
|
||||
var reactRoot = ReactMount.findReactContainerForID(id);
|
||||
return ReactInstanceHandles.findComponentRoot(reactRoot, id);
|
||||
}
|
||||
|
||||
@@ -121,6 +121,30 @@ describe('ReactInstanceHandles', function() {
|
||||
)
|
||||
).toBe(childNodeB);
|
||||
});
|
||||
|
||||
it('should throw if a rendered element cannot be found', function() {
|
||||
var parentNode = document.createElement('table');
|
||||
var childNodeA = document.createElement('tbody');
|
||||
var childNodeB = document.createElement('tr');
|
||||
parentNode.appendChild(childNodeA);
|
||||
childNodeA.appendChild(childNodeB);
|
||||
|
||||
ReactID.setID(parentNode, '.react[0]');
|
||||
// No ID on `childNodeA`, it was "rendered by the browser".
|
||||
ReactID.setID(childNodeB, '.react[0].1:0');
|
||||
|
||||
expect(function() {
|
||||
ReactInstanceHandles.findComponentRoot(
|
||||
parentNode,
|
||||
ReactID.getID(childNodeB)
|
||||
);
|
||||
}).toThrow(
|
||||
'Invariant Violation: findComponentRoot: Unable to find element by ' +
|
||||
'React ID, `.react[0].1:0`. This indicates that someone (or the ' +
|
||||
'browser) has mutated the DOM tree in an unexpected way. Try ' +
|
||||
'inspecting the child nodes of the element with React ID, `.react[0]`.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getReactRootIDFromNodeID', function() {
|
||||
|
||||
Reference in New Issue
Block a user