findReactContainerForID: function(id) { var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id); var container = containersByReactRootID[reactRootID]; if (__DEV__) { var rootElement = rootElementsByReactRootID[reactRootID]; if (rootElement && rootElement.parentNode !== container) { invariant( // 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 { console.warn( 'ReactMount: Root element has been removed from its original ' + 'container. New container:', rootElement.parentNode ); } } } return container; },
function findParent(node) { var nodeID = ReactMount.getID(node); var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID); var container = ReactMount.findReactContainerForID(rootID); var parent = ReactMount.getFirstReactDOM(container); return parent; }
// New browsers have a path attribute on native events function handleTopLevelWithPath(bookKeeping) { var path = bookKeeping.nativeEvent.path; var currentNativeTarget = path[0]; for (var i = 0; i < path.length; i++) { var currentPathElement = path[i]; var currentPathElementID = ReactMount.getID(currentPathElement); if (currentPathElement.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE) { currentNativeTarget = path[i + 1]; } if (ReactMount.isRenderedByReact(currentPathElement)) { var newRootID = ReactInstanceHandles.getReactRootIDFromNodeID( currentPathElementID ); bookKeeping.ancestors.push(currentPathElement); var topLevelTargetID = ReactMount.getID(currentPathElement) || ''; ReactEventListener._handleTopLevel( bookKeeping.topLevelType, currentPathElement, topLevelTargetID, bookKeeping.nativeEvent, currentNativeTarget ); // Jump to the root of this React render tree while (currentPathElementID !== newRootID) { i++; currentPathElement = path[i]; currentPathElementID = ReactMount.getID(currentPathElement); } } } }
/** * Finds the parent React component of `node`. * * @param {*} node * @return {?DOMEventTarget} Parent container, or `null` if the specified node * is not nested. */ function findParent(node) { // TODO: It may be a good idea to cache this to prevent unnecessary DOM // traversal, but caching is difficult to do correctly without using a // mutation observer to listen for all DOM changes. var nodeID = ReactMount.getID(node); var rootID = ReactInstanceHandles.getReactRootIDFromNodeID(nodeID); var container = ReactMount.findReactContainerForID(rootID); var parent = ReactMount.getFirstReactDOM(container); return parent; }
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; },
registerContainer: function(container) { var reactRootID = getReactRootID(container); if (reactRootID) { // If one exists, make sure it is a valid "reactRoot" ID. reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID); } if (!reactRootID) { // No valid "reactRoot" ID found, create one. reactRootID = ReactInstanceHandles.createReactRootID(); } containersByReactRootID[reactRootID] = container; return reactRootID; },
findReactContainerForID: function(id) { var reactRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id); var container = containersByReactRootID[reactRootID]; if (__DEV__) { var rootElement = rootElementsByReactRootID[reactRootID]; if (rootElement && rootElement.parentNode !== container) { console.warn( "ReactMount: Root element has been removed from its original " + "container. New container:", rootElement.parentNode ); } } return container; },
/** * 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; }
findReactContainerForID: function(id) { var reatRootID = ReactInstanceHandles.getReactRootIDFromNodeID(id); // TODO: Consider throwing if `id` is not a valid React element ID. return containersByReactRootID[reatRootID]; },
it('should support strings', function() { var test = '.reactRoot[s_0_1][0]..[1]'; var expected = '.reactRoot[s_0_1]'; var actual = ReactInstanceHandles.getReactRootIDFromNodeID(test); expect(actual).toEqual(expected); });
unmountComponentAtNode: function(container) { // Various parts of our code (such as ReactCompositeComponent's // _renderValidatedComponent) assume that calls to render aren't nested; // verify that that's the case. (Strictly speaking, unmounting won't cause a // render but we still don't expect to be in a render call here.) warning( ReactCurrentOwner.current == null, 'unmountComponentAtNode(): Render methods should be a pure function ' + 'of props and state; triggering nested component updates from render ' + 'is not allowed. If necessary, trigger nested updates in ' + 'componentDidUpdate. Check the render method of %s.', ReactCurrentOwner.current && ReactCurrentOwner.current.getName() || 'ReactCompositeComponent' ); invariant( container && ( container.nodeType === ELEMENT_NODE_TYPE || container.nodeType === DOC_NODE_TYPE || container.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE ), 'unmountComponentAtNode(...): Target container is not a DOM element.' ); var reactRootID = getReactRootID(container); var component = instancesByReactRootID[reactRootID]; if (!component) { // 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); if (__DEV__) { warning( !containerHasNonRootReactChild, 'unmountComponentAtNode(): The node you\'re attempting to unmount ' + 'was rendered by React and is not a top-level container. %s', ( isContainerReactRoot ? 'You may have accidentally passed in a React root node instead ' + 'of its container.' : 'Instead, have the parent component update its state and ' + 'rerender in order to remove this component.' ) ); } return false; } ReactUpdates.batchedUpdates( unmountComponentFromNode, component, container ); delete instancesByReactRootID[reactRootID]; delete containersByReactRootID[reactRootID]; if (__DEV__) { delete rootElementsByReactRootID[reactRootID]; } return true; },
/** * True if the supplied DOM node has a direct React-rendered child that is * not a React root element. Useful for warning in `render`, * `unmountComponentAtNode`, etc. * * @param {?DOMElement} node The candidate DOM node. * @return {boolean} True if the DOM element contains a direct child that was * rendered by React but is not a root element. * @internal */ function hasNonRootReactChild(node) { var reactRootID = getReactRootID(node); return reactRootID ? reactRootID !== ReactInstanceHandles.getReactRootIDFromNodeID(reactRootID) : false; }