react-dom
Version:
React package for working with the DOM.
273 lines (239 loc) • 11 kB
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.
*
*
*/
;
var _prodInvariant = require('./reactProdInvariant');
var ReactBrowserEventEmitter = require('./ReactBrowserEventEmitter');
var ReactControlledComponent = require('./ReactControlledComponent');
var ReactDOMComponentTree = require('./ReactDOMComponentTree');
var ReactDOMFeatureFlags = require('./ReactDOMFeatureFlags');
var ReactDOMFiberComponent = require('./ReactDOMFiberComponent');
var ReactDOMInjection = require('./ReactDOMInjection');
var ReactGenericBatching = require('./ReactGenericBatching');
var ReactFiberReconciler = require('./ReactFiberReconciler');
var ReactInputSelection = require('./ReactInputSelection');
var ReactInstanceMap = require('./ReactInstanceMap');
var ReactPortal = require('./ReactPortal');
var findDOMNode = require('./findDOMNode');
var invariant = require('fbjs/lib/invariant');
var warning = require('fbjs/lib/warning');
var createElement = ReactDOMFiberComponent.createElement,
getChildNamespace = ReactDOMFiberComponent.getChildNamespace,
setInitialProperties = ReactDOMFiberComponent.setInitialProperties,
updateProperties = ReactDOMFiberComponent.updateProperties;
var precacheFiberNode = ReactDOMComponentTree.precacheFiberNode;
if (process.env.NODE_ENV !== 'production') {
var validateDOMNesting = require('./validateDOMNesting');
var updatedAncestorInfo = validateDOMNesting.updatedAncestorInfo;
}
var DOCUMENT_NODE = 9;
ReactDOMInjection.inject();
ReactControlledComponent.injection.injectFiberControlledHostComponent(ReactDOMFiberComponent);
findDOMNode._injectFiber(function (fiber) {
return DOMRenderer.findHostInstance(fiber);
});
var ELEMENT_NODE_TYPE = 1;
var DOC_NODE_TYPE = 9;
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;
/**
* 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));
}
function validateContainer(container) {
if (!isValidContainer(container)) {
throw new Error('Target container is not a DOM element.');
}
}
function shouldAutoFocusHostComponent(type, props) {
switch (type) {
case 'button':
case 'input':
case 'select':
case 'textarea':
return !!props.autoFocus;
}
return false;
}
var DOMRenderer = ReactFiberReconciler({
getRootHostContext: function (rootContainerInstance) {
var type = rootContainerInstance.tagName.toLowerCase();
if (process.env.NODE_ENV !== 'production') {
var _namespace = getChildNamespace(null, type);
var isMountingIntoDocument = rootContainerInstance.ownerDocument.documentElement === rootContainerInstance;
var _ancestorInfo = updatedAncestorInfo(null, isMountingIntoDocument ? '#document' : type, null);
return { namespace: _namespace, ancestorInfo: _ancestorInfo };
}
return getChildNamespace(null, type);
},
getChildHostContext: function (parentHostContext, type) {
if (process.env.NODE_ENV !== 'production') {
var parentHostContextDev = parentHostContext;
var _namespace2 = getChildNamespace(parentHostContextDev.namespace, type);
var _ancestorInfo2 = updatedAncestorInfo(parentHostContextDev.ancestorInfo, type, null);
return { namespace: _namespace2, ancestorInfo: _ancestorInfo2 };
}
var parentNamespace = parentHostContext;
return getChildNamespace(parentNamespace, type);
},
prepareForCommit: function () {
var eventsEnabled = ReactBrowserEventEmitter.isEnabled();
ReactBrowserEventEmitter.setEnabled(false);
return {
eventsEnabled: eventsEnabled,
selectionInformation: ReactInputSelection.getSelectionInformation()
};
},
resetAfterCommit: function (commitInfo) {
ReactInputSelection.restoreSelection(commitInfo.selectionInformation);
ReactBrowserEventEmitter.setEnabled(commitInfo.eventsEnabled);
},
createInstance: function (type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
var parentNamespace = void 0;
if (process.env.NODE_ENV !== 'production') {
// TODO: take namespace into account when validating.
var hostContextDev = hostContext;
validateDOMNesting(type, null, null, hostContextDev.ancestorInfo);
if (typeof props.children === 'string' || typeof props.children === 'number') {
var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type, null);
validateDOMNesting(null, String(props.children), null, ownAncestorInfo);
}
parentNamespace = hostContextDev.namespace;
} else {
parentNamespace = hostContext;
}
var domElement = createElement(type, props, rootContainerInstance, parentNamespace);
precacheFiberNode(internalInstanceHandle, domElement);
return domElement;
},
appendInitialChild: function (parentInstance, child) {
parentInstance.appendChild(child);
},
finalizeInitialChildren: function (domElement, type, props, rootContainerInstance) {
setInitialProperties(domElement, type, props, rootContainerInstance);
return shouldAutoFocusHostComponent(type, props);
},
prepareUpdate: function (domElement, type, oldProps, newProps, hostContext) {
if (process.env.NODE_ENV !== 'production') {
var hostContextDev = hostContext;
if (typeof newProps.children !== typeof oldProps.children && (typeof newProps.children === 'string' || typeof newProps.children === 'number')) {
var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type, null);
validateDOMNesting(null, String(newProps.children), null, ownAncestorInfo);
}
}
return true;
},
commitMount: function (domElement, type, newProps, rootContainerInstance, internalInstanceHandle) {
if (shouldAutoFocusHostComponent(type, newProps)) {
domElement.focus();
}
},
commitUpdate: function (domElement, type, oldProps, newProps, rootContainerInstance, internalInstanceHandle) {
// Update the internal instance handle so that we know which props are
// the current ones.
precacheFiberNode(internalInstanceHandle, domElement);
updateProperties(domElement, type, oldProps, newProps, rootContainerInstance);
},
shouldSetTextContent: function (props) {
return typeof props.children === 'string' || typeof props.children === 'number' || typeof props.dangerouslySetInnerHTML === 'object' && props.dangerouslySetInnerHTML !== null && typeof props.dangerouslySetInnerHTML.__html === 'string';
},
resetTextContent: function (domElement) {
domElement.textContent = '';
},
createTextInstance: function (text, rootContainerInstance, hostContext, internalInstanceHandle) {
if (process.env.NODE_ENV !== 'production') {
var hostContextDev = hostContext;
validateDOMNesting(null, text, null, hostContextDev.ancestorInfo);
}
var textNode = document.createTextNode(text);
precacheFiberNode(internalInstanceHandle, textNode);
return textNode;
},
commitTextUpdate: function (textInstance, oldText, newText) {
textInstance.nodeValue = newText;
},
appendChild: function (parentInstance, child) {
parentInstance.appendChild(child);
},
insertBefore: function (parentInstance, child, beforeChild) {
parentInstance.insertBefore(child, beforeChild);
},
removeChild: function (parentInstance, child) {
parentInstance.removeChild(child);
},
scheduleAnimationCallback: window.requestAnimationFrame,
scheduleDeferredCallback: window.requestIdleCallback,
useSyncScheduling: true
});
ReactGenericBatching.injection.injectFiberBatchedUpdates(DOMRenderer.batchedUpdates);
var warned = false;
function warnAboutUnstableUse() {
// Ignore this warning is the feature flag is turned on. E.g. for tests.
process.env.NODE_ENV !== 'production' ? warning(warned || ReactDOMFeatureFlags.useFiber, 'You are using React DOM Fiber which is an experimental renderer. ' + 'It is likely to have bugs, breaking changes and is unsupported.') : void 0;
warned = true;
}
function renderSubtreeIntoContainer(parentComponent, children, containerNode, callback) {
validateContainer(containerNode);
var container = containerNode.nodeType === DOCUMENT_NODE ? containerNode.documentElement : containerNode;
var root = container._reactRootContainer;
if (!root) {
(function () {
// First clear any existing content.
while (container.lastChild) {
container.removeChild(container.lastChild);
}
var newRoot = DOMRenderer.createContainer(container);
root = container._reactRootContainer = newRoot;
// Initial mount should not be batched.
DOMRenderer.unbatchedUpdates(function () {
DOMRenderer.updateContainer(children, newRoot, parentComponent, callback);
});
})();
} else {
DOMRenderer.updateContainer(children, root, parentComponent, callback);
}
return DOMRenderer.getPublicRootInstance(root);
}
var ReactDOM = {
render: function (element, container, callback) {
validateContainer(container);
return renderSubtreeIntoContainer(null, element, container, callback);
},
unstable_renderSubtreeIntoContainer: function (parentComponent, element, containerNode, 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 renderSubtreeIntoContainer(parentComponent, element, containerNode, callback);
},
unmountComponentAtNode: function (container) {
!isValidContainer(container) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'unmountComponentAtNode(...): Target container is not a DOM element.') : _prodInvariant('40') : void 0;
warnAboutUnstableUse();
if (container._reactRootContainer) {
// Unmount should not be batched.
return DOMRenderer.unbatchedUpdates(function () {
return renderSubtreeIntoContainer(null, null, container, function () {
container._reactRootContainer = null;
});
});
}
},
findDOMNode: findDOMNode,
unstable_createPortal: function (children, container) {
var key = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
// TODO: pass ReactDOM portal implementation as third argument
return ReactPortal.createPortal(children, container, null, key);
},
unstable_batchedUpdates: ReactGenericBatching.batchedUpdates,
unstable_deferredUpdates: DOMRenderer.deferredUpdates
};
module.exports = ReactDOM;