UNPKG

react-dom

Version:

React package for working with the DOM.

273 lines (239 loc) • 11 kB
/** * 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 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;