537 lines
25 KiB
JavaScript
537 lines
25 KiB
JavaScript
|
/**
|
||
|
* Copyright 2013-present, Facebook, Inc.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* This source code is licensed under the BSD-style license found in the
|
||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
var _prodInvariant = require('./reactProdInvariant');
|
||
|
|
||
|
var DOMLazyTree = require('./DOMLazyTree');
|
||
|
var DOMProperty = require('./DOMProperty');
|
||
|
var React = require('react/lib/React');
|
||
|
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
|
||
|
var ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
|
||
|
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
|
||
|
var ReactDOMContainerInfo = require('./ReactDOMContainerInfo');
|
||
|
var ReactDOMFeatureFlags = require('./ReactDOMFeatureFlags');
|
||
|
var ReactFeatureFlags = require('./ReactFeatureFlags');
|
||
|
var ReactInstanceMap = require('./ReactInstanceMap');
|
||
|
var ReactInstrumentation = require('./ReactInstrumentation');
|
||
|
var ReactMarkupChecksum = require('./ReactMarkupChecksum');
|
||
|
var ReactReconciler = require('./ReactReconciler');
|
||
|
var ReactUpdateQueue = require('./ReactUpdateQueue');
|
||
|
var ReactUpdates = require('./ReactUpdates');
|
||
|
|
||
|
var emptyObject = require('fbjs/lib/emptyObject');
|
||
|
var instantiateReactComponent = require('./instantiateReactComponent');
|
||
|
var invariant = require('fbjs/lib/invariant');
|
||
|
var setInnerHTML = require('./setInnerHTML');
|
||
|
var shouldUpdateReactComponent = require('./shouldUpdateReactComponent');
|
||
|
var warning = require('fbjs/lib/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;
|
||
|
|
||
|
var instancesByReactRootID = {};
|
||
|
|
||
|
/**
|
||
|
* Finds the index of the first character
|
||
|
* that's not common between the two given strings.
|
||
|
*
|
||
|
* @return {number} the index of the character where the strings diverge
|
||
|
*/
|
||
|
function firstDifferenceIndex(string1, string2) {
|
||
|
var minLen = Math.min(string1.length, string2.length);
|
||
|
for (var i = 0; i < minLen; i++) {
|
||
|
if (string1.charAt(i) !== string2.charAt(i)) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return string1.length === string2.length ? -1 : minLen;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param {DOMElement|DOMDocument} container DOM element that may contain
|
||
|
* a React component
|
||
|
* @return {?*} DOM element that may have the reactRoot ID, or null.
|
||
|
*/
|
||
|
function getReactRootElementInContainer(container) {
|
||
|
if (!container) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
if (container.nodeType === DOC_NODE_TYPE) {
|
||
|
return container.documentElement;
|
||
|
} else {
|
||
|
return container.firstChild;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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.getAttribute && node.getAttribute(ATTR_NAME) || '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Mounts this component and inserts it into the DOM.
|
||
|
*
|
||
|
* @param {ReactComponent} componentInstance The instance to mount.
|
||
|
* @param {DOMElement} container DOM element to mount into.
|
||
|
* @param {ReactReconcileTransaction} transaction
|
||
|
* @param {boolean} shouldReuseMarkup If true, do not insert markup
|
||
|
*/
|
||
|
function mountComponentIntoNode(wrapperInstance, container, transaction, shouldReuseMarkup, context) {
|
||
|
var markerName;
|
||
|
if (ReactFeatureFlags.logTopLevelRenders) {
|
||
|
var wrappedElement = wrapperInstance._currentElement.props.child;
|
||
|
var type = wrappedElement.type;
|
||
|
markerName = 'React mount: ' + (typeof type === 'string' ? type : type.displayName || type.name);
|
||
|
console.time(markerName);
|
||
|
}
|
||
|
|
||
|
var markup = ReactReconciler.mountComponent(wrapperInstance, transaction, null, ReactDOMContainerInfo(wrapperInstance, container), context, 0 /* parentDebugID */
|
||
|
);
|
||
|
|
||
|
if (markerName) {
|
||
|
console.timeEnd(markerName);
|
||
|
}
|
||
|
|
||
|
wrapperInstance._renderedComponent._topLevelWrapper = wrapperInstance;
|
||
|
ReactMount._mountImageIntoNode(markup, container, wrapperInstance, shouldReuseMarkup, transaction);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Batched mount.
|
||
|
*
|
||
|
* @param {ReactComponent} componentInstance The instance to mount.
|
||
|
* @param {DOMElement} container DOM element to mount into.
|
||
|
* @param {boolean} shouldReuseMarkup If true, do not insert markup
|
||
|
*/
|
||
|
function batchedMountComponentIntoNode(componentInstance, container, shouldReuseMarkup, context) {
|
||
|
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(
|
||
|
/* useCreateElement */
|
||
|
!shouldReuseMarkup && ReactDOMFeatureFlags.useCreateElement);
|
||
|
transaction.perform(mountComponentIntoNode, null, componentInstance, container, transaction, shouldReuseMarkup, context);
|
||
|
ReactUpdates.ReactReconcileTransaction.release(transaction);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unmounts a component and removes it from the DOM.
|
||
|
*
|
||
|
* @param {ReactComponent} instance React component instance.
|
||
|
* @param {DOMElement} container DOM element to unmount from.
|
||
|
* @final
|
||
|
* @internal
|
||
|
* @see {ReactMount.unmountComponentAtNode}
|
||
|
*/
|
||
|
function unmountComponentFromNode(instance, container, safely) {
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
ReactInstrumentation.debugTool.onBeginFlush();
|
||
|
}
|
||
|
ReactReconciler.unmountComponent(instance, safely);
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
ReactInstrumentation.debugTool.onEndFlush();
|
||
|
}
|
||
|
|
||
|
if (container.nodeType === DOC_NODE_TYPE) {
|
||
|
container = container.documentElement;
|
||
|
}
|
||
|
|
||
|
// http://jsperf.com/emptying-a-node
|
||
|
while (container.lastChild) {
|
||
|
container.removeChild(container.lastChild);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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(container) {
|
||
|
var rootEl = getReactRootElementInContainer(container);
|
||
|
if (rootEl) {
|
||
|
var inst = ReactDOMComponentTree.getInstanceFromNode(rootEl);
|
||
|
return !!(inst && inst._hostParent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* True if the supplied DOM node is a React DOM element and
|
||
|
* it has been rendered by another copy of React.
|
||
|
*
|
||
|
* @param {?DOMElement} node The candidate DOM node.
|
||
|
* @return {boolean} True if the DOM has been rendered by another copy of React
|
||
|
* @internal
|
||
|
*/
|
||
|
function nodeIsRenderedByOtherInstance(container) {
|
||
|
var rootEl = getReactRootElementInContainer(container);
|
||
|
return !!(rootEl && isReactNode(rootEl) && !ReactDOMComponentTree.getInstanceFromNode(rootEl));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* True if the supplied DOM node is a valid node element.
|
||
|
*
|
||
|
* @param {?DOMElement} node The candidate DOM node.
|
||
|
* @return {boolean} True if the DOM is a valid DOM node.
|
||
|
* @internal
|
||
|
*/
|
||
|
function isValidContainer(node) {
|
||
|
return !!(node && (node.nodeType === ELEMENT_NODE_TYPE || node.nodeType === DOC_NODE_TYPE || node.nodeType === DOCUMENT_FRAGMENT_NODE_TYPE));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* True if the supplied DOM node is a valid React node element.
|
||
|
*
|
||
|
* @param {?DOMElement} node The candidate DOM node.
|
||
|
* @return {boolean} True if the DOM is a valid React DOM node.
|
||
|
* @internal
|
||
|
*/
|
||
|
function isReactNode(node) {
|
||
|
return isValidContainer(node) && (node.hasAttribute(ROOT_ATTR_NAME) || node.hasAttribute(ATTR_NAME));
|
||
|
}
|
||
|
|
||
|
function getHostRootInstanceInContainer(container) {
|
||
|
var rootEl = getReactRootElementInContainer(container);
|
||
|
var prevHostInstance = rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl);
|
||
|
return prevHostInstance && !prevHostInstance._hostParent ? prevHostInstance : null;
|
||
|
}
|
||
|
|
||
|
function getTopLevelWrapperInContainer(container) {
|
||
|
var root = getHostRootInstanceInContainer(container);
|
||
|
return root ? root._hostContainerInfo._topLevelWrapper : 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
|
||
|
* here.
|
||
|
*/
|
||
|
var topLevelRootCounter = 1;
|
||
|
var TopLevelWrapper = function () {
|
||
|
this.rootID = topLevelRootCounter++;
|
||
|
};
|
||
|
TopLevelWrapper.prototype.isReactComponent = {};
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
TopLevelWrapper.displayName = 'TopLevelWrapper';
|
||
|
}
|
||
|
TopLevelWrapper.prototype.render = function () {
|
||
|
return this.props.child;
|
||
|
};
|
||
|
TopLevelWrapper.isReactTopLevelWrapper = true;
|
||
|
|
||
|
/**
|
||
|
* Mounting is the process of initializing a React component by creating its
|
||
|
* representative DOM elements and inserting them into a supplied `container`.
|
||
|
* Any prior content inside `container` is destroyed in the process.
|
||
|
*
|
||
|
* ReactMount.render(
|
||
|
* component,
|
||
|
* document.getElementById('container')
|
||
|
* );
|
||
|
*
|
||
|
* <div id="container"> <-- Supplied `container`.
|
||
|
* <div data-reactid=".3"> <-- Rendered reactRoot of React
|
||
|
* // ... component.
|
||
|
* </div>
|
||
|
* </div>
|
||
|
*
|
||
|
* Inside of `container`, the first element rendered is the "reactRoot".
|
||
|
*/
|
||
|
var ReactMount = {
|
||
|
|
||
|
TopLevelWrapper: TopLevelWrapper,
|
||
|
|
||
|
/**
|
||
|
* Used by devtools. The keys are not important.
|
||
|
*/
|
||
|
_instancesByReactRootID: instancesByReactRootID,
|
||
|
|
||
|
/**
|
||
|
* This is a hook provided to support rendering React components while
|
||
|
* ensuring that the apparent scroll position of its `container` does not
|
||
|
* change.
|
||
|
*
|
||
|
* @param {DOMElement} container The `container` being rendered into.
|
||
|
* @param {function} renderCallback This must be called once to do the render.
|
||
|
*/
|
||
|
scrollMonitor: function (container, renderCallback) {
|
||
|
renderCallback();
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Take a component that's already mounted into the DOM and replace its props
|
||
|
* @param {ReactComponent} prevComponent component instance already in the DOM
|
||
|
* @param {ReactElement} nextElement component instance to render
|
||
|
* @param {DOMElement} container container to render into
|
||
|
* @param {?function} callback function triggered on completion
|
||
|
*/
|
||
|
_updateRootComponent: function (prevComponent, nextElement, nextContext, container, callback) {
|
||
|
ReactMount.scrollMonitor(container, function () {
|
||
|
ReactUpdateQueue.enqueueElementInternal(prevComponent, nextElement, nextContext);
|
||
|
if (callback) {
|
||
|
ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return prevComponent;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Render a new component into the DOM. Hooked by hooks!
|
||
|
*
|
||
|
* @param {ReactElement} nextElement element to render
|
||
|
* @param {DOMElement} container container to render into
|
||
|
* @param {boolean} shouldReuseMarkup if we should skip the markup insertion
|
||
|
* @return {ReactComponent} nextComponent
|
||
|
*/
|
||
|
_renderNewRootComponent: function (nextElement, container, shouldReuseMarkup, context) {
|
||
|
// Various parts of our code (such as ReactCompositeComponent's
|
||
|
// _renderValidatedComponent) assume that calls to render aren't nested;
|
||
|
// verify that that's the case.
|
||
|
process.env.NODE_ENV !== 'production' ? warning(ReactCurrentOwner.current == null, '_renderNewRootComponent(): 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') : void 0;
|
||
|
|
||
|
!isValidContainer(container) ? process.env.NODE_ENV !== 'production' ? invariant(false, '_registerComponent(...): Target container is not a DOM element.') : _prodInvariant('37') : void 0;
|
||
|
|
||
|
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
|
||
|
var componentInstance = instantiateReactComponent(nextElement, false);
|
||
|
|
||
|
// 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.
|
||
|
|
||
|
ReactUpdates.batchedUpdates(batchedMountComponentIntoNode, componentInstance, container, shouldReuseMarkup, context);
|
||
|
|
||
|
var wrapperID = componentInstance._instance.rootID;
|
||
|
instancesByReactRootID[wrapperID] = componentInstance;
|
||
|
|
||
|
return componentInstance;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Renders a React component into the DOM in the supplied `container`.
|
||
|
*
|
||
|
* If the React component was previously rendered into `container`, this will
|
||
|
* perform an update on it and only mutate the DOM as necessary to reflect the
|
||
|
* latest React component.
|
||
|
*
|
||
|
* @param {ReactComponent} parentComponent The conceptual parent of this render tree.
|
||
|
* @param {ReactElement} nextElement Component element to render.
|
||
|
* @param {DOMElement} container DOM element to render into.
|
||
|
* @param {?function} callback function triggered on completion
|
||
|
* @return {ReactComponent} Component instance rendered in `container`.
|
||
|
*/
|
||
|
renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
|
||
|
!(parentComponent != null && ReactInstanceMap.has(parentComponent)) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'parentComponent must be a valid React Component') : _prodInvariant('38') : void 0;
|
||
|
return ReactMount._renderSubtreeIntoContainer(parentComponent, nextElement, container, callback);
|
||
|
},
|
||
|
|
||
|
_renderSubtreeIntoContainer: function (parentComponent, nextElement, container, callback) {
|
||
|
ReactUpdateQueue.validateCallback(callback, 'ReactDOM.render');
|
||
|
!React.isValidElement(nextElement) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'ReactDOM.render(): Invalid component element.%s', typeof nextElement === 'string' ? ' Instead of passing a string like \'div\', pass ' + 'React.createElement(\'div\') or <div />.' : typeof nextElement === 'function' ? ' Instead of passing a class like Foo, pass ' + 'React.createElement(Foo) or <Foo />.' :
|
||
|
// Check if it quacks like an element
|
||
|
nextElement != null && nextElement.props !== undefined ? ' This may be caused by unintentionally loading two independent ' + 'copies of React.' : '') : _prodInvariant('39', typeof nextElement === 'string' ? ' Instead of passing a string like \'div\', pass ' + 'React.createElement(\'div\') or <div />.' : typeof nextElement === 'function' ? ' Instead of passing a class like Foo, pass ' + 'React.createElement(Foo) or <Foo />.' : nextElement != null && nextElement.props !== undefined ? ' This may be caused by unintentionally loading two independent ' + 'copies of React.' : '') : void 0;
|
||
|
|
||
|
process.env.NODE_ENV !== 'production' ? warning(!container || !container.tagName || container.tagName.toUpperCase() !== 'BODY', 'render(): Rendering components directly into document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try rendering into a container element created ' + 'for your app.') : void 0;
|
||
|
|
||
|
var nextWrappedElement = React.createElement(TopLevelWrapper, { child: nextElement });
|
||
|
|
||
|
var nextContext;
|
||
|
if (parentComponent) {
|
||
|
var parentInst = ReactInstanceMap.get(parentComponent);
|
||
|
nextContext = parentInst._processChildContext(parentInst._context);
|
||
|
} else {
|
||
|
nextContext = emptyObject;
|
||
|
}
|
||
|
|
||
|
var prevComponent = getTopLevelWrapperInContainer(container);
|
||
|
|
||
|
if (prevComponent) {
|
||
|
var prevWrappedElement = prevComponent._currentElement;
|
||
|
var prevElement = prevWrappedElement.props.child;
|
||
|
if (shouldUpdateReactComponent(prevElement, nextElement)) {
|
||
|
var publicInst = prevComponent._renderedComponent.getPublicInstance();
|
||
|
var updatedCallback = callback && function () {
|
||
|
callback.call(publicInst);
|
||
|
};
|
||
|
ReactMount._updateRootComponent(prevComponent, nextWrappedElement, nextContext, container, updatedCallback);
|
||
|
return publicInst;
|
||
|
} else {
|
||
|
ReactMount.unmountComponentAtNode(container);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var reactRootElement = getReactRootElementInContainer(container);
|
||
|
var containerHasReactMarkup = reactRootElement && !!internalGetID(reactRootElement);
|
||
|
var containerHasNonRootReactChild = hasNonRootReactChild(container);
|
||
|
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
process.env.NODE_ENV !== 'production' ? warning(!containerHasNonRootReactChild, 'render(...): Replacing React-rendered children with a new root ' + 'component. If you intended to update the children of this node, ' + 'you should instead have the existing children update their state ' + 'and render the new components instead of calling ReactDOM.render.') : void 0;
|
||
|
|
||
|
if (!containerHasReactMarkup || reactRootElement.nextSibling) {
|
||
|
var rootElementSibling = reactRootElement;
|
||
|
while (rootElementSibling) {
|
||
|
if (internalGetID(rootElementSibling)) {
|
||
|
process.env.NODE_ENV !== 'production' ? warning(false, 'render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.') : void 0;
|
||
|
break;
|
||
|
}
|
||
|
rootElementSibling = rootElementSibling.nextSibling;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var shouldReuseMarkup = containerHasReactMarkup && !prevComponent && !containerHasNonRootReactChild;
|
||
|
var component = ReactMount._renderNewRootComponent(nextWrappedElement, container, shouldReuseMarkup, nextContext)._renderedComponent.getPublicInstance();
|
||
|
if (callback) {
|
||
|
callback.call(component);
|
||
|
}
|
||
|
return component;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Renders a React component into the DOM in the supplied `container`.
|
||
|
* See https://facebook.github.io/react/docs/top-level-api.html#reactdom.render
|
||
|
*
|
||
|
* If the React component was previously rendered into `container`, this will
|
||
|
* perform an update on it and only mutate the DOM as necessary to reflect the
|
||
|
* latest React component.
|
||
|
*
|
||
|
* @param {ReactElement} nextElement Component element to render.
|
||
|
* @param {DOMElement} container DOM element to render into.
|
||
|
* @param {?function} callback function triggered on completion
|
||
|
* @return {ReactComponent} Component instance rendered in `container`.
|
||
|
*/
|
||
|
render: function (nextElement, container, callback) {
|
||
|
return ReactMount._renderSubtreeIntoContainer(null, nextElement, container, callback);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Unmounts and destroys the React component rendered in the `container`.
|
||
|
* See https://facebook.github.io/react/docs/top-level-api.html#reactdom.unmountcomponentatnode
|
||
|
*
|
||
|
* @param {DOMElement} container DOM element containing a React component.
|
||
|
* @return {boolean} True if a component was found in and unmounted from
|
||
|
* `container`
|
||
|
*/
|
||
|
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.)
|
||
|
process.env.NODE_ENV !== 'production' ? 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') : void 0;
|
||
|
|
||
|
!isValidContainer(container) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'unmountComponentAtNode(...): Target container is not a DOM element.') : _prodInvariant('40') : void 0;
|
||
|
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
process.env.NODE_ENV !== 'production' ? warning(!nodeIsRenderedByOtherInstance(container), 'unmountComponentAtNode(): The node you\'re attempting to unmount ' + 'was rendered by another copy of React.') : void 0;
|
||
|
}
|
||
|
|
||
|
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 isContainerReactRoot = container.nodeType === 1 && container.hasAttribute(ROOT_ATTR_NAME);
|
||
|
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
process.env.NODE_ENV !== 'production' ? 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.') : void 0;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
delete instancesByReactRootID[prevComponent._instance.rootID];
|
||
|
ReactUpdates.batchedUpdates(unmountComponentFromNode, prevComponent, container, false);
|
||
|
return true;
|
||
|
},
|
||
|
|
||
|
_mountImageIntoNode: function (markup, container, instance, shouldReuseMarkup, transaction) {
|
||
|
!isValidContainer(container) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'mountComponentIntoNode(...): Target container is not valid.') : _prodInvariant('41') : void 0;
|
||
|
|
||
|
if (shouldReuseMarkup) {
|
||
|
var rootElement = getReactRootElementInContainer(container);
|
||
|
if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
|
||
|
ReactDOMComponentTree.precacheNode(instance, rootElement);
|
||
|
return;
|
||
|
} else {
|
||
|
var checksum = rootElement.getAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
|
||
|
rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
|
||
|
|
||
|
var rootMarkup = rootElement.outerHTML;
|
||
|
rootElement.setAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME, checksum);
|
||
|
|
||
|
var normalizedMarkup = markup;
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
// because rootMarkup is retrieved from the DOM, various normalizations
|
||
|
// will have occurred which will not be present in `markup`. Here,
|
||
|
// insert markup into a <div> or <iframe> depending on the container
|
||
|
// type to perform the same normalizations before comparing.
|
||
|
var normalizer;
|
||
|
if (container.nodeType === ELEMENT_NODE_TYPE) {
|
||
|
normalizer = document.createElement('div');
|
||
|
normalizer.innerHTML = markup;
|
||
|
normalizedMarkup = normalizer.innerHTML;
|
||
|
} else {
|
||
|
normalizer = document.createElement('iframe');
|
||
|
document.body.appendChild(normalizer);
|
||
|
normalizer.contentDocument.write(markup);
|
||
|
normalizedMarkup = normalizer.contentDocument.documentElement.outerHTML;
|
||
|
document.body.removeChild(normalizer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var diffIndex = firstDifferenceIndex(normalizedMarkup, rootMarkup);
|
||
|
var difference = ' (client) ' + normalizedMarkup.substring(diffIndex - 20, diffIndex + 20) + '\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20);
|
||
|
|
||
|
!(container.nodeType !== DOC_NODE_TYPE) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'You\'re trying to render a component to the document using server rendering but the checksum was invalid. This usually means you rendered a different component type or props on the client from the one on the server, or your render() methods are impure. React cannot handle this case due to cross-browser 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%s', difference) : _prodInvariant('42', difference) : void 0;
|
||
|
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
process.env.NODE_ENV !== 'production' ? warning(false, 'React attempted to reuse markup in a container but the ' + 'checksum was invalid. This generally means that you are ' + 'using server rendering and the markup generated on the ' + 'server was not what the client was expecting. React injected ' + 'new markup to compensate which works but you have lost many ' + 'of the benefits of server rendering. Instead, figure out ' + 'why the markup being generated is different on the client ' + 'or server:\n%s', difference) : void 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
!(container.nodeType !== DOC_NODE_TYPE) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'You\'re trying to render a component to the document but you didn\'t use server rendering. We can\'t do this without using server rendering due to cross-browser quirks. See ReactDOMServer.renderToString() for server rendering.') : _prodInvariant('43') : void 0;
|
||
|
|
||
|
if (transaction.useCreateElement) {
|
||
|
while (container.lastChild) {
|
||
|
container.removeChild(container.lastChild);
|
||
|
}
|
||
|
DOMLazyTree.insertTreeBefore(container, markup, null);
|
||
|
} else {
|
||
|
setInnerHTML(container, markup);
|
||
|
ReactDOMComponentTree.precacheNode(instance, container.firstChild);
|
||
|
}
|
||
|
|
||
|
if (process.env.NODE_ENV !== 'production') {
|
||
|
var hostNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild);
|
||
|
if (hostNode._debugID !== 0) {
|
||
|
ReactInstrumentation.debugTool.onHostOperation({
|
||
|
instanceID: hostNode._debugID,
|
||
|
type: 'mount',
|
||
|
payload: markup.toString()
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
module.exports = ReactMount;
|