/** @license React v16.2.0 * react-dom.development.js * * Copyright (c) 2013-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; if (process.env.NODE_ENV !== "production") { (function() { 'use strict'; var React = require('react'); var invariant = require('fbjs/lib/invariant'); var warning = require('fbjs/lib/warning'); var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment'); var _assign = require('object-assign'); var emptyFunction = require('fbjs/lib/emptyFunction'); var EventListener = require('fbjs/lib/EventListener'); var getActiveElement = require('fbjs/lib/getActiveElement'); var shallowEqual = require('fbjs/lib/shallowEqual'); var containsNode = require('fbjs/lib/containsNode'); var focusNode = require('fbjs/lib/focusNode'); var emptyObject = require('fbjs/lib/emptyObject'); var checkPropTypes = require('prop-types/checkPropTypes'); var hyphenateStyleName = require('fbjs/lib/hyphenateStyleName'); var camelizeStyleName = require('fbjs/lib/camelizeStyleName'); /** * WARNING: DO NOT manually require this module. * This is a replacement for `invariant(...)` used by the error code system * and will _only_ be required by the corresponding babel pass. * It always throws. */ !React ? invariant(false, 'ReactDOM was loaded before React. Make sure you load the React package before loading ReactDOM.') : void 0; // These attributes should be all lowercase to allow for // case insensitive checks var RESERVED_PROPS = { children: true, dangerouslySetInnerHTML: true, defaultValue: true, defaultChecked: true, innerHTML: true, suppressContentEditableWarning: true, suppressHydrationWarning: true, style: true }; function checkMask(value, bitmask) { return (value & bitmask) === bitmask; } var DOMPropertyInjection = { /** * Mapping from normalized, camelcased property names to a configuration that * specifies how the associated DOM property should be accessed or rendered. */ MUST_USE_PROPERTY: 0x1, HAS_BOOLEAN_VALUE: 0x4, HAS_NUMERIC_VALUE: 0x8, HAS_POSITIVE_NUMERIC_VALUE: 0x10 | 0x8, HAS_OVERLOADED_BOOLEAN_VALUE: 0x20, HAS_STRING_BOOLEAN_VALUE: 0x40, /** * Inject some specialized knowledge about the DOM. This takes a config object * with the following properties: * * Properties: object mapping DOM property name to one of the * DOMPropertyInjection constants or null. If your attribute isn't in here, * it won't get written to the DOM. * * DOMAttributeNames: object mapping React attribute name to the DOM * attribute name. Attribute names not specified use the **lowercase** * normalized name. * * DOMAttributeNamespaces: object mapping React attribute name to the DOM * attribute namespace URL. (Attribute names not specified use no namespace.) * * DOMPropertyNames: similar to DOMAttributeNames but for DOM properties. * Property names not specified use the normalized name. * * DOMMutationMethods: Properties that require special mutation methods. If * `value` is undefined, the mutation method should unset the property. * * @param {object} domPropertyConfig the config as described above. */ injectDOMPropertyConfig: function (domPropertyConfig) { var Injection = DOMPropertyInjection; var Properties = domPropertyConfig.Properties || {}; var DOMAttributeNamespaces = domPropertyConfig.DOMAttributeNamespaces || {}; var DOMAttributeNames = domPropertyConfig.DOMAttributeNames || {}; var DOMMutationMethods = domPropertyConfig.DOMMutationMethods || {}; for (var propName in Properties) { !!properties.hasOwnProperty(propName) ? invariant(false, "injectDOMPropertyConfig(...): You're trying to inject DOM property '%s' which has already been injected. You may be accidentally injecting the same DOM property config twice, or you may be injecting two configs that have conflicting property names.", propName) : void 0; var lowerCased = propName.toLowerCase(); var propConfig = Properties[propName]; var propertyInfo = { attributeName: lowerCased, attributeNamespace: null, propertyName: propName, mutationMethod: null, mustUseProperty: checkMask(propConfig, Injection.MUST_USE_PROPERTY), hasBooleanValue: checkMask(propConfig, Injection.HAS_BOOLEAN_VALUE), hasNumericValue: checkMask(propConfig, Injection.HAS_NUMERIC_VALUE), hasPositiveNumericValue: checkMask(propConfig, Injection.HAS_POSITIVE_NUMERIC_VALUE), hasOverloadedBooleanValue: checkMask(propConfig, Injection.HAS_OVERLOADED_BOOLEAN_VALUE), hasStringBooleanValue: checkMask(propConfig, Injection.HAS_STRING_BOOLEAN_VALUE) }; !(propertyInfo.hasBooleanValue + propertyInfo.hasNumericValue + propertyInfo.hasOverloadedBooleanValue <= 1) ? invariant(false, "DOMProperty: Value can be one of boolean, overloaded boolean, or numeric value, but not a combination: %s", propName) : void 0; if (DOMAttributeNames.hasOwnProperty(propName)) { var attributeName = DOMAttributeNames[propName]; propertyInfo.attributeName = attributeName; } if (DOMAttributeNamespaces.hasOwnProperty(propName)) { propertyInfo.attributeNamespace = DOMAttributeNamespaces[propName]; } if (DOMMutationMethods.hasOwnProperty(propName)) { propertyInfo.mutationMethod = DOMMutationMethods[propName]; } // Downcase references to whitelist properties to check for membership // without case-sensitivity. This allows the whitelist to pick up // `allowfullscreen`, which should be written using the property configuration // for `allowFullscreen` properties[propName] = propertyInfo; } } }; /* eslint-disable max-len */ var ATTRIBUTE_NAME_START_CHAR = ":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; /* eslint-enable max-len */ var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + "\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; var ROOT_ATTRIBUTE_NAME = 'data-reactroot'; /** * Map from property "standard name" to an object with info about how to set * the property in the DOM. Each object contains: * * attributeName: * Used when rendering markup or with `*Attribute()`. * attributeNamespace * propertyName: * Used on DOM node instances. (This includes properties that mutate due to * external factors.) * mutationMethod: * If non-null, used instead of the property or `setAttribute()` after * initial render. * mustUseProperty: * Whether the property must be accessed and mutated as an object property. * hasBooleanValue: * Whether the property should be removed when set to a falsey value. * hasNumericValue: * Whether the property must be numeric or parse as a numeric and should be * removed when set to a falsey value. * hasPositiveNumericValue: * Whether the property must be positive numeric or parse as a positive * numeric and should be removed when set to a falsey value. * hasOverloadedBooleanValue: * Whether the property can be used as a flag as well as with a value. * Removed when strictly equal to false; present without a value when * strictly equal to true; present with a value otherwise. */ var properties = {}; /** * Checks whether a property name is a writeable attribute. * @method */ function shouldSetAttribute(name, value) { if (isReservedProp(name)) { return false; } if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) { return false; } if (value === null) { return true; } switch (typeof value) { case 'boolean': return shouldAttributeAcceptBooleanValue(name); case 'undefined': case 'number': case 'string': case 'object': return true; default: // function, symbol return false; } } function getPropertyInfo(name) { return properties.hasOwnProperty(name) ? properties[name] : null; } function shouldAttributeAcceptBooleanValue(name) { if (isReservedProp(name)) { return true; } var propertyInfo = getPropertyInfo(name); if (propertyInfo) { return propertyInfo.hasBooleanValue || propertyInfo.hasStringBooleanValue || propertyInfo.hasOverloadedBooleanValue; } var prefix = name.toLowerCase().slice(0, 5); return prefix === 'data-' || prefix === 'aria-'; } /** * Checks to see if a property name is within the list of properties * reserved for internal React operations. These properties should * not be set on an HTML element. * * @private * @param {string} name * @return {boolean} If the name is within reserved props */ function isReservedProp(name) { return RESERVED_PROPS.hasOwnProperty(name); } var injection = DOMPropertyInjection; var MUST_USE_PROPERTY = injection.MUST_USE_PROPERTY; var HAS_BOOLEAN_VALUE = injection.HAS_BOOLEAN_VALUE; var HAS_NUMERIC_VALUE = injection.HAS_NUMERIC_VALUE; var HAS_POSITIVE_NUMERIC_VALUE = injection.HAS_POSITIVE_NUMERIC_VALUE; var HAS_OVERLOADED_BOOLEAN_VALUE = injection.HAS_OVERLOADED_BOOLEAN_VALUE; var HAS_STRING_BOOLEAN_VALUE = injection.HAS_STRING_BOOLEAN_VALUE; var HTMLDOMPropertyConfig = { // When adding attributes to this list, be sure to also add them to // the `possibleStandardNames` module to ensure casing and incorrect // name warnings. Properties: { allowFullScreen: HAS_BOOLEAN_VALUE, // specifies target context for links with `preload` type async: HAS_BOOLEAN_VALUE, // Note: there is a special case that prevents it from being written to the DOM // on the client side because the browsers are inconsistent. Instead we call focus(). autoFocus: HAS_BOOLEAN_VALUE, autoPlay: HAS_BOOLEAN_VALUE, capture: HAS_OVERLOADED_BOOLEAN_VALUE, checked: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, cols: HAS_POSITIVE_NUMERIC_VALUE, contentEditable: HAS_STRING_BOOLEAN_VALUE, controls: HAS_BOOLEAN_VALUE, 'default': HAS_BOOLEAN_VALUE, defer: HAS_BOOLEAN_VALUE, disabled: HAS_BOOLEAN_VALUE, download: HAS_OVERLOADED_BOOLEAN_VALUE, draggable: HAS_STRING_BOOLEAN_VALUE, formNoValidate: HAS_BOOLEAN_VALUE, hidden: HAS_BOOLEAN_VALUE, loop: HAS_BOOLEAN_VALUE, // Caution; `option.selected` is not updated if `select.multiple` is // disabled with `removeAttribute`. multiple: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, muted: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, noValidate: HAS_BOOLEAN_VALUE, open: HAS_BOOLEAN_VALUE, playsInline: HAS_BOOLEAN_VALUE, readOnly: HAS_BOOLEAN_VALUE, required: HAS_BOOLEAN_VALUE, reversed: HAS_BOOLEAN_VALUE, rows: HAS_POSITIVE_NUMERIC_VALUE, rowSpan: HAS_NUMERIC_VALUE, scoped: HAS_BOOLEAN_VALUE, seamless: HAS_BOOLEAN_VALUE, selected: MUST_USE_PROPERTY | HAS_BOOLEAN_VALUE, size: HAS_POSITIVE_NUMERIC_VALUE, start: HAS_NUMERIC_VALUE, // support for projecting regular DOM Elements via V1 named slots ( shadow dom ) span: HAS_POSITIVE_NUMERIC_VALUE, spellCheck: HAS_STRING_BOOLEAN_VALUE, // Style must be explicitly set in the attribute list. React components // expect a style object style: 0, // Keep it in the whitelist because it is case-sensitive for SVG. tabIndex: 0, // itemScope is for for Microdata support. // See http://schema.org/docs/gs.html itemScope: HAS_BOOLEAN_VALUE, // These attributes must stay in the white-list because they have // different attribute names (see DOMAttributeNames below) acceptCharset: 0, className: 0, htmlFor: 0, httpEquiv: 0, // Attributes with mutation methods must be specified in the whitelist // Set the string boolean flag to allow the behavior value: HAS_STRING_BOOLEAN_VALUE }, DOMAttributeNames: { acceptCharset: 'accept-charset', className: 'class', htmlFor: 'for', httpEquiv: 'http-equiv' }, DOMMutationMethods: { value: function (node, value) { if (value == null) { return node.removeAttribute('value'); } // Number inputs get special treatment due to some edge cases in // Chrome. Let everything else assign the value attribute as normal. // https://github.com/facebook/react/issues/7253#issuecomment-236074326 if (node.type !== 'number' || node.hasAttribute('value') === false) { node.setAttribute('value', '' + value); } else if (node.validity && !node.validity.badInput && node.ownerDocument.activeElement !== node) { // Don't assign an attribute if validation reports bad // input. Chrome will clear the value. Additionally, don't // operate on inputs that have focus, otherwise Chrome might // strip off trailing decimal places and cause the user's // cursor position to jump to the beginning of the input. // // In ReactDOMInput, we have an onBlur event that will trigger // this function again when focus is lost. node.setAttribute('value', '' + value); } } } }; var HAS_STRING_BOOLEAN_VALUE$1 = injection.HAS_STRING_BOOLEAN_VALUE; var NS = { xlink: 'http://www.w3.org/1999/xlink', xml: 'http://www.w3.org/XML/1998/namespace' }; /** * This is a list of all SVG attributes that need special casing, * namespacing, or boolean value assignment. * * When adding attributes to this list, be sure to also add them to * the `possibleStandardNames` module to ensure casing and incorrect * name warnings. * * SVG Attributes List: * https://www.w3.org/TR/SVG/attindex.html * SMIL Spec: * https://www.w3.org/TR/smil */ var ATTRS = ['accent-height', 'alignment-baseline', 'arabic-form', 'baseline-shift', 'cap-height', 'clip-path', 'clip-rule', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'dominant-baseline', 'enable-background', 'fill-opacity', 'fill-rule', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-name', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'horiz-adv-x', 'horiz-origin-x', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'overline-position', 'overline-thickness', 'paint-order', 'panose-1', 'pointer-events', 'rendering-intent', 'shape-rendering', 'stop-color', 'stop-opacity', 'strikethrough-position', 'strikethrough-thickness', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'underline-position', 'underline-thickness', 'unicode-bidi', 'unicode-range', 'units-per-em', 'v-alphabetic', 'v-hanging', 'v-ideographic', 'v-mathematical', 'vector-effect', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'word-spacing', 'writing-mode', 'x-height', 'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xmlns:xlink', 'xml:lang', 'xml:space']; var SVGDOMPropertyConfig = { Properties: { autoReverse: HAS_STRING_BOOLEAN_VALUE$1, externalResourcesRequired: HAS_STRING_BOOLEAN_VALUE$1, preserveAlpha: HAS_STRING_BOOLEAN_VALUE$1 }, DOMAttributeNames: { autoReverse: 'autoReverse', externalResourcesRequired: 'externalResourcesRequired', preserveAlpha: 'preserveAlpha' }, DOMAttributeNamespaces: { xlinkActuate: NS.xlink, xlinkArcrole: NS.xlink, xlinkHref: NS.xlink, xlinkRole: NS.xlink, xlinkShow: NS.xlink, xlinkTitle: NS.xlink, xlinkType: NS.xlink, xmlBase: NS.xml, xmlLang: NS.xml, xmlSpace: NS.xml } }; var CAMELIZE = /[\-\:]([a-z])/g; var capitalize = function (token) { return token[1].toUpperCase(); }; ATTRS.forEach(function (original) { var reactName = original.replace(CAMELIZE, capitalize); SVGDOMPropertyConfig.Properties[reactName] = 0; SVGDOMPropertyConfig.DOMAttributeNames[reactName] = original; }); injection.injectDOMPropertyConfig(HTMLDOMPropertyConfig); injection.injectDOMPropertyConfig(SVGDOMPropertyConfig); var ReactErrorUtils = { // Used by Fiber to simulate a try-catch. _caughtError: null, _hasCaughtError: false, // Used by event system to capture/rethrow the first error. _rethrowError: null, _hasRethrowError: false, injection: { injectErrorUtils: function (injectedErrorUtils) { !(typeof injectedErrorUtils.invokeGuardedCallback === 'function') ? invariant(false, 'Injected invokeGuardedCallback() must be a function.') : void 0; invokeGuardedCallback = injectedErrorUtils.invokeGuardedCallback; } }, /** * Call a function while guarding against errors that happens within it. * Returns an error if it throws, otherwise null. * * In production, this is implemented using a try-catch. The reason we don't * use a try-catch directly is so that we can swap out a different * implementation in DEV mode. * * @param {String} name of the guard to use for logging or debugging * @param {Function} func The function to invoke * @param {*} context The context to use when calling the function * @param {...*} args Arguments for function */ invokeGuardedCallback: function (name, func, context, a, b, c, d, e, f) { invokeGuardedCallback.apply(ReactErrorUtils, arguments); }, /** * Same as invokeGuardedCallback, but instead of returning an error, it stores * it in a global so it can be rethrown by `rethrowCaughtError` later. * TODO: See if _caughtError and _rethrowError can be unified. * * @param {String} name of the guard to use for logging or debugging * @param {Function} func The function to invoke * @param {*} context The context to use when calling the function * @param {...*} args Arguments for function */ invokeGuardedCallbackAndCatchFirstError: function (name, func, context, a, b, c, d, e, f) { ReactErrorUtils.invokeGuardedCallback.apply(this, arguments); if (ReactErrorUtils.hasCaughtError()) { var error = ReactErrorUtils.clearCaughtError(); if (!ReactErrorUtils._hasRethrowError) { ReactErrorUtils._hasRethrowError = true; ReactErrorUtils._rethrowError = error; } } }, /** * During execution of guarded functions we will capture the first error which * we will rethrow to be handled by the top level error handler. */ rethrowCaughtError: function () { return rethrowCaughtError.apply(ReactErrorUtils, arguments); }, hasCaughtError: function () { return ReactErrorUtils._hasCaughtError; }, clearCaughtError: function () { if (ReactErrorUtils._hasCaughtError) { var error = ReactErrorUtils._caughtError; ReactErrorUtils._caughtError = null; ReactErrorUtils._hasCaughtError = false; return error; } else { invariant(false, 'clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue.'); } } }; var invokeGuardedCallback = function (name, func, context, a, b, c, d, e, f) { ReactErrorUtils._hasCaughtError = false; ReactErrorUtils._caughtError = null; var funcArgs = Array.prototype.slice.call(arguments, 3); try { func.apply(context, funcArgs); } catch (error) { ReactErrorUtils._caughtError = error; ReactErrorUtils._hasCaughtError = true; } }; { // In DEV mode, we swap out invokeGuardedCallback for a special version // that plays more nicely with the browser's DevTools. The idea is to preserve // "Pause on exceptions" behavior. Because React wraps all user-provided // functions in invokeGuardedCallback, and the production version of // invokeGuardedCallback uses a try-catch, all user exceptions are treated // like caught exceptions, and the DevTools won't pause unless the developer // takes the extra step of enabling pause on caught exceptions. This is // untintuitive, though, because even though React has caught the error, from // the developer's perspective, the error is uncaught. // // To preserve the expected "Pause on exceptions" behavior, we don't use a // try-catch in DEV. Instead, we synchronously dispatch a fake event to a fake // DOM node, and call the user-provided callback from inside an event handler // for that fake event. If the callback throws, the error is "captured" using // a global event handler. But because the error happens in a different // event loop context, it does not interrupt the normal program flow. // Effectively, this gives us try-catch behavior without actually using // try-catch. Neat! // Check that the browser supports the APIs we need to implement our special // DEV version of invokeGuardedCallback if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') { var fakeNode = document.createElement('react'); var invokeGuardedCallbackDev = function (name, func, context, a, b, c, d, e, f) { // Keeps track of whether the user-provided callback threw an error. We // set this to true at the beginning, then set it to false right after // calling the function. If the function errors, `didError` will never be // set to false. This strategy works even if the browser is flaky and // fails to call our global error handler, because it doesn't rely on // the error event at all. var didError = true; // Create an event handler for our fake event. We will synchronously // dispatch our fake event using `dispatchEvent`. Inside the handler, we // call the user-provided callback. var funcArgs = Array.prototype.slice.call(arguments, 3); function callCallback() { // We immediately remove the callback from event listeners so that // nested `invokeGuardedCallback` calls do not clash. Otherwise, a // nested call would trigger the fake event handlers of any call higher // in the stack. fakeNode.removeEventListener(evtType, callCallback, false); func.apply(context, funcArgs); didError = false; } // Create a global error event handler. We use this to capture the value // that was thrown. It's possible that this error handler will fire more // than once; for example, if non-React code also calls `dispatchEvent` // and a handler for that event throws. We should be resilient to most of // those cases. Even if our error event handler fires more than once, the // last error event is always used. If the callback actually does error, // we know that the last error event is the correct one, because it's not // possible for anything else to have happened in between our callback // erroring and the code that follows the `dispatchEvent` call below. If // the callback doesn't error, but the error event was fired, we know to // ignore it because `didError` will be false, as described above. var error = void 0; // Use this to track whether the error event is ever called. var didSetError = false; var isCrossOriginError = false; function onError(event) { error = event.error; didSetError = true; if (error === null && event.colno === 0 && event.lineno === 0) { isCrossOriginError = true; } } // Create a fake event type. var evtType = 'react-' + (name ? name : 'invokeguardedcallback'); // Attach our event handlers window.addEventListener('error', onError); fakeNode.addEventListener(evtType, callCallback, false); // Synchronously dispatch our fake event. If the user-provided function // errors, it will trigger our global error handler. var evt = document.createEvent('Event'); evt.initEvent(evtType, false, false); fakeNode.dispatchEvent(evt); if (didError) { if (!didSetError) { // The callback errored, but the error event never fired. error = new Error('An error was thrown inside one of your components, but React ' + "doesn't know what it was. This is likely due to browser " + 'flakiness. React does its best to preserve the "Pause on ' + 'exceptions" behavior of the DevTools, which requires some ' + "DEV-mode only tricks. It's possible that these don't work in " + 'your browser. Try triggering the error in production mode, ' + 'or switching to a modern browser. If you suspect that this is ' + 'actually an issue with React, please file an issue.'); } else if (isCrossOriginError) { error = new Error("A cross-origin error was thrown. React doesn't have access to " + 'the actual error object in development. ' + 'See https://fb.me/react-crossorigin-error for more information.'); } ReactErrorUtils._hasCaughtError = true; ReactErrorUtils._caughtError = error; } else { ReactErrorUtils._hasCaughtError = false; ReactErrorUtils._caughtError = null; } // Remove our event listeners window.removeEventListener('error', onError); }; invokeGuardedCallback = invokeGuardedCallbackDev; } } var rethrowCaughtError = function () { if (ReactErrorUtils._hasRethrowError) { var error = ReactErrorUtils._rethrowError; ReactErrorUtils._rethrowError = null; ReactErrorUtils._hasRethrowError = false; throw error; } }; /** * Injectable ordering of event plugins. */ var eventPluginOrder = null; /** * Injectable mapping from names to event plugin modules. */ var namesToPlugins = {}; /** * Recomputes the plugin list using the injected plugins and plugin ordering. * * @private */ function recomputePluginOrdering() { if (!eventPluginOrder) { // Wait until an `eventPluginOrder` is injected. return; } for (var pluginName in namesToPlugins) { var pluginModule = namesToPlugins[pluginName]; var pluginIndex = eventPluginOrder.indexOf(pluginName); !(pluginIndex > -1) ? invariant(false, 'EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `%s`.', pluginName) : void 0; if (plugins[pluginIndex]) { continue; } !pluginModule.extractEvents ? invariant(false, 'EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `%s` does not.', pluginName) : void 0; plugins[pluginIndex] = pluginModule; var publishedEvents = pluginModule.eventTypes; for (var eventName in publishedEvents) { !publishEventForPlugin(publishedEvents[eventName], pluginModule, eventName) ? invariant(false, 'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.', eventName, pluginName) : void 0; } } } /** * Publishes an event so that it can be dispatched by the supplied plugin. * * @param {object} dispatchConfig Dispatch configuration for the event. * @param {object} PluginModule Plugin publishing the event. * @return {boolean} True if the event was successfully published. * @private */ function publishEventForPlugin(dispatchConfig, pluginModule, eventName) { !!eventNameDispatchConfigs.hasOwnProperty(eventName) ? invariant(false, 'EventPluginHub: More than one plugin attempted to publish the same event name, `%s`.', eventName) : void 0; eventNameDispatchConfigs[eventName] = dispatchConfig; var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames; if (phasedRegistrationNames) { for (var phaseName in phasedRegistrationNames) { if (phasedRegistrationNames.hasOwnProperty(phaseName)) { var phasedRegistrationName = phasedRegistrationNames[phaseName]; publishRegistrationName(phasedRegistrationName, pluginModule, eventName); } } return true; } else if (dispatchConfig.registrationName) { publishRegistrationName(dispatchConfig.registrationName, pluginModule, eventName); return true; } return false; } /** * Publishes a registration name that is used to identify dispatched events. * * @param {string} registrationName Registration name to add. * @param {object} PluginModule Plugin publishing the event. * @private */ function publishRegistrationName(registrationName, pluginModule, eventName) { !!registrationNameModules[registrationName] ? invariant(false, 'EventPluginHub: More than one plugin attempted to publish the same registration name, `%s`.', registrationName) : void 0; registrationNameModules[registrationName] = pluginModule; registrationNameDependencies[registrationName] = pluginModule.eventTypes[eventName].dependencies; { var lowerCasedName = registrationName.toLowerCase(); possibleRegistrationNames[lowerCasedName] = registrationName; if (registrationName === 'onDoubleClick') { possibleRegistrationNames.ondblclick = registrationName; } } } /** * Registers plugins so that they can extract and dispatch events. * * @see {EventPluginHub} */ /** * Ordered list of injected plugins. */ var plugins = []; /** * Mapping from event name to dispatch config */ var eventNameDispatchConfigs = {}; /** * Mapping from registration name to plugin module */ var registrationNameModules = {}; /** * Mapping from registration name to event name */ var registrationNameDependencies = {}; /** * Mapping from lowercase registration names to the properly cased version, * used to warn in the case of missing event handlers. Available * only in true. * @type {Object} */ var possibleRegistrationNames = {}; // Trust the developer to only use possibleRegistrationNames in true /** * Injects an ordering of plugins (by plugin name). This allows the ordering * to be decoupled from injection of the actual plugins so that ordering is * always deterministic regardless of packaging, on-the-fly injection, etc. * * @param {array} InjectedEventPluginOrder * @internal * @see {EventPluginHub.injection.injectEventPluginOrder} */ function injectEventPluginOrder(injectedEventPluginOrder) { !!eventPluginOrder ? invariant(false, 'EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React.') : void 0; // Clone the ordering so it cannot be dynamically mutated. eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder); recomputePluginOrdering(); } /** * Injects plugins to be used by `EventPluginHub`. The plugin names must be * in the ordering injected by `injectEventPluginOrder`. * * Plugins can be injected as part of page initialization or on-the-fly. * * @param {object} injectedNamesToPlugins Map from names to plugin modules. * @internal * @see {EventPluginHub.injection.injectEventPluginsByName} */ function injectEventPluginsByName(injectedNamesToPlugins) { var isOrderingDirty = false; for (var pluginName in injectedNamesToPlugins) { if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) { continue; } var pluginModule = injectedNamesToPlugins[pluginName]; if (!namesToPlugins.hasOwnProperty(pluginName) || namesToPlugins[pluginName] !== pluginModule) { !!namesToPlugins[pluginName] ? invariant(false, 'EventPluginRegistry: Cannot inject two different event plugins using the same name, `%s`.', pluginName) : void 0; namesToPlugins[pluginName] = pluginModule; isOrderingDirty = true; } } if (isOrderingDirty) { recomputePluginOrdering(); } } var EventPluginRegistry = Object.freeze({ plugins: plugins, eventNameDispatchConfigs: eventNameDispatchConfigs, registrationNameModules: registrationNameModules, registrationNameDependencies: registrationNameDependencies, possibleRegistrationNames: possibleRegistrationNames, injectEventPluginOrder: injectEventPluginOrder, injectEventPluginsByName: injectEventPluginsByName }); var getFiberCurrentPropsFromNode = null; var getInstanceFromNode = null; var getNodeFromInstance = null; var injection$2 = { injectComponentTree: function (Injected) { getFiberCurrentPropsFromNode = Injected.getFiberCurrentPropsFromNode; getInstanceFromNode = Injected.getInstanceFromNode; getNodeFromInstance = Injected.getNodeFromInstance; { warning(getNodeFromInstance && getInstanceFromNode, 'EventPluginUtils.injection.injectComponentTree(...): Injected ' + 'module is missing getNodeFromInstance or getInstanceFromNode.'); } } }; var validateEventDispatches; { validateEventDispatches = function (event) { var dispatchListeners = event._dispatchListeners; var dispatchInstances = event._dispatchInstances; var listenersIsArr = Array.isArray(dispatchListeners); var listenersLen = listenersIsArr ? dispatchListeners.length : dispatchListeners ? 1 : 0; var instancesIsArr = Array.isArray(dispatchInstances); var instancesLen = instancesIsArr ? dispatchInstances.length : dispatchInstances ? 1 : 0; warning(instancesIsArr === listenersIsArr && instancesLen === listenersLen, 'EventPluginUtils: Invalid `event`.'); }; } /** * Dispatch the event to the listener. * @param {SyntheticEvent} event SyntheticEvent to handle * @param {boolean} simulated If the event is simulated (changes exn behavior) * @param {function} listener Application-level callback * @param {*} inst Internal component instance */ function executeDispatch(event, simulated, listener, inst) { var type = event.type || 'unknown-event'; event.currentTarget = getNodeFromInstance(inst); ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event); event.currentTarget = null; } /** * Standard/simple iteration through an event's collected dispatches. */ function executeDispatchesInOrder(event, simulated) { var dispatchListeners = event._dispatchListeners; var dispatchInstances = event._dispatchInstances; { validateEventDispatches(event); } if (Array.isArray(dispatchListeners)) { for (var i = 0; i < dispatchListeners.length; i++) { if (event.isPropagationStopped()) { break; } // Listeners and Instances are two parallel arrays that are always in sync. executeDispatch(event, simulated, dispatchListeners[i], dispatchInstances[i]); } } else if (dispatchListeners) { executeDispatch(event, simulated, dispatchListeners, dispatchInstances); } event._dispatchListeners = null; event._dispatchInstances = null; } /** * @see executeDispatchesInOrderStopAtTrueImpl */ /** * Execution of a "direct" dispatch - there must be at most one dispatch * accumulated on the event or it is considered an error. It doesn't really make * sense for an event with multiple dispatches (bubbled) to keep track of the * return values at each dispatch execution, but it does tend to make sense when * dealing with "direct" dispatches. * * @return {*} The return value of executing the single dispatch. */ /** * @param {SyntheticEvent} event * @return {boolean} True iff number of dispatches accumulated is greater than 0. */ /** * Accumulates items that must not be null or undefined into the first one. This * is used to conserve memory by avoiding array allocations, and thus sacrifices * API cleanness. Since `current` can be null before being passed in and not * null after this function, make sure to assign it back to `current`: * * `a = accumulateInto(a, b);` * * This API should be sparingly used. Try `accumulate` for something cleaner. * * @return {*|array<*>} An accumulation of items. */ function accumulateInto(current, next) { !(next != null) ? invariant(false, 'accumulateInto(...): Accumulated items must not be null or undefined.') : void 0; if (current == null) { return next; } // Both are not empty. Warning: Never call x.concat(y) when you are not // certain that x is an Array (x could be a string with concat method). if (Array.isArray(current)) { if (Array.isArray(next)) { current.push.apply(current, next); return current; } current.push(next); return current; } if (Array.isArray(next)) { // A bit too dangerous to mutate `next`. return [current].concat(next); } return [current, next]; } /** * @param {array} arr an "accumulation" of items which is either an Array or * a single item. Useful when paired with the `accumulate` module. This is a * simple utility that allows us to reason about a collection of items, but * handling the case when there is exactly one item (and we do not need to * allocate an array). * @param {function} cb Callback invoked with each element or a collection. * @param {?} [scope] Scope used as `this` in a callback. */ function forEachAccumulated(arr, cb, scope) { if (Array.isArray(arr)) { arr.forEach(cb, scope); } else if (arr) { cb.call(scope, arr); } } /** * Internal queue of events that have accumulated their dispatches and are * waiting to have their dispatches executed. */ var eventQueue = null; /** * Dispatches an event and releases it back into the pool, unless persistent. * * @param {?object} event Synthetic event to be dispatched. * @param {boolean} simulated If the event is simulated (changes exn behavior) * @private */ var executeDispatchesAndRelease = function (event, simulated) { if (event) { executeDispatchesInOrder(event, simulated); if (!event.isPersistent()) { event.constructor.release(event); } } }; var executeDispatchesAndReleaseSimulated = function (e) { return executeDispatchesAndRelease(e, true); }; var executeDispatchesAndReleaseTopLevel = function (e) { return executeDispatchesAndRelease(e, false); }; function isInteractive(tag) { return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea'; } function shouldPreventMouseEvent(name, type, props) { switch (name) { case 'onClick': case 'onClickCapture': case 'onDoubleClick': case 'onDoubleClickCapture': case 'onMouseDown': case 'onMouseDownCapture': case 'onMouseMove': case 'onMouseMoveCapture': case 'onMouseUp': case 'onMouseUpCapture': return !!(props.disabled && isInteractive(type)); default: return false; } } /** * This is a unified interface for event plugins to be installed and configured. * * Event plugins can implement the following properties: * * `extractEvents` {function(string, DOMEventTarget, string, object): *} * Required. When a top-level event is fired, this method is expected to * extract synthetic events that will in turn be queued and dispatched. * * `eventTypes` {object} * Optional, plugins that fire events must publish a mapping of registration * names that are used to register listeners. Values of this mapping must * be objects that contain `registrationName` or `phasedRegistrationNames`. * * `executeDispatch` {function(object, function, string)} * Optional, allows plugins to override how an event gets dispatched. By * default, the listener is simply invoked. * * Each plugin that is injected into `EventsPluginHub` is immediately operable. * * @public */ /** * Methods for injecting dependencies. */ var injection$1 = { /** * @param {array} InjectedEventPluginOrder * @public */ injectEventPluginOrder: injectEventPluginOrder, /** * @param {object} injectedNamesToPlugins Map from names to plugin modules. */ injectEventPluginsByName: injectEventPluginsByName }; /** * @param {object} inst The instance, which is the source of events. * @param {string} registrationName Name of listener (e.g. `onClick`). * @return {?function} The stored callback. */ function getListener(inst, registrationName) { var listener; // TODO: shouldPreventMouseEvent is DOM-specific and definitely should not // live here; needs to be moved to a better place soon var stateNode = inst.stateNode; if (!stateNode) { // Work in progress (ex: onload events in incremental mode). return null; } var props = getFiberCurrentPropsFromNode(stateNode); if (!props) { // Work in progress. return null; } listener = props[registrationName]; if (shouldPreventMouseEvent(registrationName, inst.type, props)) { return null; } !(!listener || typeof listener === 'function') ? invariant(false, 'Expected `%s` listener to be a function, instead got a value of `%s` type.', registrationName, typeof listener) : void 0; return listener; } /** * Allows registered plugins an opportunity to extract events from top-level * native browser events. * * @return {*} An accumulation of synthetic events. * @internal */ function extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget) { var events; for (var i = 0; i < plugins.length; i++) { // Not every plugin in the ordering may be loaded at runtime. var possiblePlugin = plugins[i]; if (possiblePlugin) { var extractedEvents = possiblePlugin.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget); if (extractedEvents) { events = accumulateInto(events, extractedEvents); } } } return events; } /** * Enqueues a synthetic event that should be dispatched when * `processEventQueue` is invoked. * * @param {*} events An accumulation of synthetic events. * @internal */ function enqueueEvents(events) { if (events) { eventQueue = accumulateInto(eventQueue, events); } } /** * Dispatches all synthetic events on the event queue. * * @internal */ function processEventQueue(simulated) { // Set `eventQueue` to null before processing it so that we can tell if more // events get enqueued while processing. var processingEventQueue = eventQueue; eventQueue = null; if (!processingEventQueue) { return; } if (simulated) { forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseSimulated); } else { forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel); } !!eventQueue ? invariant(false, 'processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented.') : void 0; // This would be a good time to rethrow if any of the event handlers threw. ReactErrorUtils.rethrowCaughtError(); } var EventPluginHub = Object.freeze({ injection: injection$1, getListener: getListener, extractEvents: extractEvents, enqueueEvents: enqueueEvents, processEventQueue: processEventQueue }); var IndeterminateComponent = 0; // Before we know whether it is functional or class var FunctionalComponent = 1; var ClassComponent = 2; var HostRoot = 3; // Root of a host tree. Could be nested inside another node. var HostPortal = 4; // A subtree. Could be an entry point to a different renderer. var HostComponent = 5; var HostText = 6; var CallComponent = 7; var CallHandlerPhase = 8; var ReturnComponent = 9; var Fragment = 10; var randomKey = Math.random().toString(36).slice(2); var internalInstanceKey = '__reactInternalInstance$' + randomKey; var internalEventHandlersKey = '__reactEventHandlers$' + randomKey; function precacheFiberNode$1(hostInst, node) { node[internalInstanceKey] = hostInst; } /** * Given a DOM node, return the closest ReactDOMComponent or * ReactDOMTextComponent instance ancestor. */ function getClosestInstanceFromNode(node) { if (node[internalInstanceKey]) { return node[internalInstanceKey]; } // Walk up the tree until we find an ancestor whose instance we have cached. var parents = []; while (!node[internalInstanceKey]) { parents.push(node); if (node.parentNode) { node = node.parentNode; } else { // Top of the tree. This node must not be part of a React tree (or is // unmounted, potentially). return null; } } var closest = void 0; var inst = node[internalInstanceKey]; if (inst.tag === HostComponent || inst.tag === HostText) { // In Fiber, this will always be the deepest root. return inst; } for (; node && (inst = node[internalInstanceKey]); node = parents.pop()) { closest = inst; } return closest; } /** * Given a DOM node, return the ReactDOMComponent or ReactDOMTextComponent * instance, or null if the node was not rendered by this React. */ function getInstanceFromNode$1(node) { var inst = node[internalInstanceKey]; if (inst) { if (inst.tag === HostComponent || inst.tag === HostText) { return inst; } else { return null; } } return null; } /** * Given a ReactDOMComponent or ReactDOMTextComponent, return the corresponding * DOM node. */ function getNodeFromInstance$1(inst) { if (inst.tag === HostComponent || inst.tag === HostText) { // In Fiber this, is just the state node right now. We assume it will be // a host component or host text. return inst.stateNode; } // Without this first invariant, passing a non-DOM-component triggers the next // invariant for a missing parent, which is super confusing. invariant(false, 'getNodeFromInstance: Invalid argument.'); } function getFiberCurrentPropsFromNode$1(node) { return node[internalEventHandlersKey] || null; } function updateFiberProps$1(node, props) { node[internalEventHandlersKey] = props; } var ReactDOMComponentTree = Object.freeze({ precacheFiberNode: precacheFiberNode$1, getClosestInstanceFromNode: getClosestInstanceFromNode, getInstanceFromNode: getInstanceFromNode$1, getNodeFromInstance: getNodeFromInstance$1, getFiberCurrentPropsFromNode: getFiberCurrentPropsFromNode$1, updateFiberProps: updateFiberProps$1 }); function getParent(inst) { do { inst = inst['return']; // TODO: If this is a HostRoot we might want to bail out. // That is depending on if we want nested subtrees (layers) to bubble // events to their parent. We could also go through parentNode on the // host node but that wouldn't work for React Native and doesn't let us // do the portal feature. } while (inst && inst.tag !== HostComponent); if (inst) { return inst; } return null; } /** * Return the lowest common ancestor of A and B, or null if they are in * different trees. */ function getLowestCommonAncestor(instA, instB) { var depthA = 0; for (var tempA = instA; tempA; tempA = getParent(tempA)) { depthA++; } var depthB = 0; for (var tempB = instB; tempB; tempB = getParent(tempB)) { depthB++; } // If A is deeper, crawl up. while (depthA - depthB > 0) { instA = getParent(instA); depthA--; } // If B is deeper, crawl up. while (depthB - depthA > 0) { instB = getParent(instB); depthB--; } // Walk in lockstep until we find a match. var depth = depthA; while (depth--) { if (instA === instB || instA === instB.alternate) { return instA; } instA = getParent(instA); instB = getParent(instB); } return null; } /** * Return if A is an ancestor of B. */ /** * Return the parent instance of the passed-in instance. */ function getParentInstance(inst) { return getParent(inst); } /** * Simulates the traversal of a two-phase, capture/bubble event dispatch. */ function traverseTwoPhase(inst, fn, arg) { var path = []; while (inst) { path.push(inst); inst = getParent(inst); } var i; for (i = path.length; i-- > 0;) { fn(path[i], 'captured', arg); } for (i = 0; i < path.length; i++) { fn(path[i], 'bubbled', arg); } } /** * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that * should would receive a `mouseEnter` or `mouseLeave` event. * * Does not invoke the callback on the nearest common ancestor because nothing * "entered" or "left" that element. */ function traverseEnterLeave(from, to, fn, argFrom, argTo) { var common = from && to ? getLowestCommonAncestor(from, to) : null; var pathFrom = []; while (true) { if (!from) { break; } if (from === common) { break; } var alternate = from.alternate; if (alternate !== null && alternate === common) { break; } pathFrom.push(from); from = getParent(from); } var pathTo = []; while (true) { if (!to) { break; } if (to === common) { break; } var _alternate = to.alternate; if (_alternate !== null && _alternate === common) { break; } pathTo.push(to); to = getParent(to); } for (var i = 0; i < pathFrom.length; i++) { fn(pathFrom[i], 'bubbled', argFrom); } for (var _i = pathTo.length; _i-- > 0;) { fn(pathTo[_i], 'captured', argTo); } } /** * Some event types have a notion of different registration names for different * "phases" of propagation. This finds listeners by a given phase. */ function listenerAtPhase(inst, event, propagationPhase) { var registrationName = event.dispatchConfig.phasedRegistrationNames[propagationPhase]; return getListener(inst, registrationName); } /** * A small set of propagation patterns, each of which will accept a small amount * of information, and generate a set of "dispatch ready event objects" - which * are sets of events that have already been annotated with a set of dispatched * listener functions/ids. The API is designed this way to discourage these * propagation strategies from actually executing the dispatches, since we * always want to collect the entire set of dispatches before executing even a * single one. */ /** * Tags a `SyntheticEvent` with dispatched listeners. Creating this function * here, allows us to not have to bind or create functions for each event. * Mutating the event's members allows us to not have to create a wrapping * "dispatch" object that pairs the event with the listener. */ function accumulateDirectionalDispatches(inst, phase, event) { { warning(inst, 'Dispatching inst must not be null'); } var listener = listenerAtPhase(inst, event, phase); if (listener) { event._dispatchListeners = accumulateInto(event._dispatchListeners, listener); event._dispatchInstances = accumulateInto(event._dispatchInstances, inst); } } /** * Collect dispatches (must be entirely collected before dispatching - see unit * tests). Lazily allocate the array to conserve memory. We must loop through * each event and perform the traversal for each one. We cannot perform a * single traversal for the entire collection of events because each event may * have a different target. */ function accumulateTwoPhaseDispatchesSingle(event) { if (event && event.dispatchConfig.phasedRegistrationNames) { traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event); } } /** * Same as `accumulateTwoPhaseDispatchesSingle`, but skips over the targetID. */ function accumulateTwoPhaseDispatchesSingleSkipTarget(event) { if (event && event.dispatchConfig.phasedRegistrationNames) { var targetInst = event._targetInst; var parentInst = targetInst ? getParentInstance(targetInst) : null; traverseTwoPhase(parentInst, accumulateDirectionalDispatches, event); } } /** * Accumulates without regard to direction, does not look for phased * registration names. Same as `accumulateDirectDispatchesSingle` but without * requiring that the `dispatchMarker` be the same as the dispatched ID. */ function accumulateDispatches(inst, ignoredDirection, event) { if (inst && event && event.dispatchConfig.registrationName) { var registrationName = event.dispatchConfig.registrationName; var listener = getListener(inst, registrationName); if (listener) { event._dispatchListeners = accumulateInto(event._dispatchListeners, listener); event._dispatchInstances = accumulateInto(event._dispatchInstances, inst); } } } /** * Accumulates dispatches on an `SyntheticEvent`, but only for the * `dispatchMarker`. * @param {SyntheticEvent} event */ function accumulateDirectDispatchesSingle(event) { if (event && event.dispatchConfig.registrationName) { accumulateDispatches(event._targetInst, null, event); } } function accumulateTwoPhaseDispatches(events) { forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle); } function accumulateTwoPhaseDispatchesSkipTarget(events) { forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget); } function accumulateEnterLeaveDispatches(leave, enter, from, to) { traverseEnterLeave(from, to, accumulateDispatches, leave, enter); } function accumulateDirectDispatches(events) { forEachAccumulated(events, accumulateDirectDispatchesSingle); } var EventPropagators = Object.freeze({ accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches, accumulateTwoPhaseDispatchesSkipTarget: accumulateTwoPhaseDispatchesSkipTarget, accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches, accumulateDirectDispatches: accumulateDirectDispatches }); var contentKey = null; /** * Gets the key used to access text content on a DOM node. * * @return {?string} Key used to access text content. * @internal */ function getTextContentAccessor() { if (!contentKey && ExecutionEnvironment.canUseDOM) { // Prefer textContent to innerText because many browsers support both but // SVG elements don't support innerText even when
does. contentKey = 'textContent' in document.documentElement ? 'textContent' : 'innerText'; } return contentKey; } /** * This helper object stores information about text content of a target node, * allowing comparison of content before and after a given event. * * Identify the node where selection currently begins, then observe * both its text content and its current position in the DOM. Since the * browser may natively replace the target node during composition, we can * use its position to find its replacement. * * */ var compositionState = { _root: null, _startText: null, _fallbackText: null }; function initialize(nativeEventTarget) { compositionState._root = nativeEventTarget; compositionState._startText = getText(); return true; } function reset() { compositionState._root = null; compositionState._startText = null; compositionState._fallbackText = null; } function getData() { if (compositionState._fallbackText) { return compositionState._fallbackText; } var start; var startValue = compositionState._startText; var startLength = startValue.length; var end; var endValue = getText(); var endLength = endValue.length; for (start = 0; start < startLength; start++) { if (startValue[start] !== endValue[start]) { break; } } var minEnd = startLength - start; for (end = 1; end <= minEnd; end++) { if (startValue[startLength - end] !== endValue[endLength - end]) { break; } } var sliceTail = end > 1 ? 1 - end : undefined; compositionState._fallbackText = endValue.slice(start, sliceTail); return compositionState._fallbackText; } function getText() { if ('value' in compositionState._root) { return compositionState._root.value; } return compositionState._root[getTextContentAccessor()]; } /* eslint valid-typeof: 0 */ var didWarnForAddedNewProperty = false; var isProxySupported = typeof Proxy === 'function'; var EVENT_POOL_SIZE = 10; var shouldBeReleasedProperties = ['dispatchConfig', '_targetInst', 'nativeEvent', 'isDefaultPrevented', 'isPropagationStopped', '_dispatchListeners', '_dispatchInstances']; /** * @interface Event * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ var EventInterface = { type: null, target: null, // currentTarget is set when dispatching; no use in copying it here currentTarget: emptyFunction.thatReturnsNull, eventPhase: null, bubbles: null, cancelable: null, timeStamp: function (event) { return event.timeStamp || Date.now(); }, defaultPrevented: null, isTrusted: null }; /** * Synthetic events are dispatched by event plugins, typically in response to a * top-level event delegation handler. * * These systems should generally use pooling to reduce the frequency of garbage * collection. The system should check `isPersistent` to determine whether the * event should be released into the pool after being dispatched. Users that * need a persisted event should invoke `persist`. * * Synthetic events (and subclasses) implement the DOM Level 3 Events API by * normalizing browser quirks. Subclasses do not necessarily have to implement a * DOM interface; custom application-specific events can also subclass this. * * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {*} targetInst Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @param {DOMEventTarget} nativeEventTarget Target node. */ function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) { { // these have a getter/setter for warnings delete this.nativeEvent; delete this.preventDefault; delete this.stopPropagation; } this.dispatchConfig = dispatchConfig; this._targetInst = targetInst; this.nativeEvent = nativeEvent; var Interface = this.constructor.Interface; for (var propName in Interface) { if (!Interface.hasOwnProperty(propName)) { continue; } { delete this[propName]; // this has a getter/setter for warnings } var normalize = Interface[propName]; if (normalize) { this[propName] = normalize(nativeEvent); } else { if (propName === 'target') { this.target = nativeEventTarget; } else { this[propName] = nativeEvent[propName]; } } } var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false; if (defaultPrevented) { this.isDefaultPrevented = emptyFunction.thatReturnsTrue; } else { this.isDefaultPrevented = emptyFunction.thatReturnsFalse; } this.isPropagationStopped = emptyFunction.thatReturnsFalse; return this; } _assign(SyntheticEvent.prototype, { preventDefault: function () { this.defaultPrevented = true; var event = this.nativeEvent; if (!event) { return; } if (event.preventDefault) { event.preventDefault(); } else if (typeof event.returnValue !== 'unknown') { event.returnValue = false; } this.isDefaultPrevented = emptyFunction.thatReturnsTrue; }, stopPropagation: function () { var event = this.nativeEvent; if (!event) { return; } if (event.stopPropagation) { event.stopPropagation(); } else if (typeof event.cancelBubble !== 'unknown') { // The ChangeEventPlugin registers a "propertychange" event for // IE. This event does not support bubbling or cancelling, and // any references to cancelBubble throw "Member not found". A // typeof check of "unknown" circumvents this issue (and is also // IE specific). event.cancelBubble = true; } this.isPropagationStopped = emptyFunction.thatReturnsTrue; }, /** * We release all dispatched `SyntheticEvent`s after each event loop, adding * them back into the pool. This allows a way to hold onto a reference that * won't be added back into the pool. */ persist: function () { this.isPersistent = emptyFunction.thatReturnsTrue; }, /** * Checks if this event should be released back into the pool. * * @return {boolean} True if this should not be released, false otherwise. */ isPersistent: emptyFunction.thatReturnsFalse, /** * `PooledClass` looks for `destructor` on each instance it releases. */ destructor: function () { var Interface = this.constructor.Interface; for (var propName in Interface) { { Object.defineProperty(this, propName, getPooledWarningPropertyDefinition(propName, Interface[propName])); } } for (var i = 0; i < shouldBeReleasedProperties.length; i++) { this[shouldBeReleasedProperties[i]] = null; } { Object.defineProperty(this, 'nativeEvent', getPooledWarningPropertyDefinition('nativeEvent', null)); Object.defineProperty(this, 'preventDefault', getPooledWarningPropertyDefinition('preventDefault', emptyFunction)); Object.defineProperty(this, 'stopPropagation', getPooledWarningPropertyDefinition('stopPropagation', emptyFunction)); } } }); SyntheticEvent.Interface = EventInterface; /** * Helper to reduce boilerplate when creating subclasses. * * @param {function} Class * @param {?object} Interface */ SyntheticEvent.augmentClass = function (Class, Interface) { var Super = this; var E = function () {}; E.prototype = Super.prototype; var prototype = new E(); _assign(prototype, Class.prototype); Class.prototype = prototype; Class.prototype.constructor = Class; Class.Interface = _assign({}, Super.Interface, Interface); Class.augmentClass = Super.augmentClass; addEventPoolingTo(Class); }; /** Proxying after everything set on SyntheticEvent * to resolve Proxy issue on some WebKit browsers * in which some Event properties are set to undefined (GH#10010) */ { if (isProxySupported) { /*eslint-disable no-func-assign */ SyntheticEvent = new Proxy(SyntheticEvent, { construct: function (target, args) { return this.apply(target, Object.create(target.prototype), args); }, apply: function (constructor, that, args) { return new Proxy(constructor.apply(that, args), { set: function (target, prop, value) { if (prop !== 'isPersistent' && !target.constructor.Interface.hasOwnProperty(prop) && shouldBeReleasedProperties.indexOf(prop) === -1) { warning(didWarnForAddedNewProperty || target.isPersistent(), "This synthetic event is reused for performance reasons. If you're " + "seeing this, you're adding a new property in the synthetic event object. " + 'The property is never released. See ' + 'https://fb.me/react-event-pooling for more information.'); didWarnForAddedNewProperty = true; } target[prop] = value; return true; } }); } }); /*eslint-enable no-func-assign */ } } addEventPoolingTo(SyntheticEvent); /** * Helper to nullify syntheticEvent instance properties when destructing * * @param {String} propName * @param {?object} getVal * @return {object} defineProperty object */ function getPooledWarningPropertyDefinition(propName, getVal) { var isFunction = typeof getVal === 'function'; return { configurable: true, set: set, get: get }; function set(val) { var action = isFunction ? 'setting the method' : 'setting the property'; warn(action, 'This is effectively a no-op'); return val; } function get() { var action = isFunction ? 'accessing the method' : 'accessing the property'; var result = isFunction ? 'This is a no-op function' : 'This is set to null'; warn(action, result); return getVal; } function warn(action, result) { var warningCondition = false; warning(warningCondition, "This synthetic event is reused for performance reasons. If you're seeing this, " + "you're %s `%s` on a released/nullified synthetic event. %s. " + 'If you must keep the original synthetic event around, use event.persist(). ' + 'See https://fb.me/react-event-pooling for more information.', action, propName, result); } } function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) { var EventConstructor = this; if (EventConstructor.eventPool.length) { var instance = EventConstructor.eventPool.pop(); EventConstructor.call(instance, dispatchConfig, targetInst, nativeEvent, nativeInst); return instance; } return new EventConstructor(dispatchConfig, targetInst, nativeEvent, nativeInst); } function releasePooledEvent(event) { var EventConstructor = this; !(event instanceof EventConstructor) ? invariant(false, 'Trying to release an event instance into a pool of a different type.') : void 0; event.destructor(); if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) { EventConstructor.eventPool.push(event); } } function addEventPoolingTo(EventConstructor) { EventConstructor.eventPool = []; EventConstructor.getPooled = getPooledEvent; EventConstructor.release = releasePooledEvent; } var SyntheticEvent$1 = SyntheticEvent; /** * @interface Event * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents */ var CompositionEventInterface = { data: null }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticEvent} */ function SyntheticCompositionEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticEvent$1.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticEvent$1.augmentClass(SyntheticCompositionEvent, CompositionEventInterface); /** * @interface Event * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105 * /#events-inputevents */ var InputEventInterface = { data: null }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticEvent} */ function SyntheticInputEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticEvent$1.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticEvent$1.augmentClass(SyntheticInputEvent, InputEventInterface); var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space var START_KEYCODE = 229; var canUseCompositionEvent = ExecutionEnvironment.canUseDOM && 'CompositionEvent' in window; var documentMode = null; if (ExecutionEnvironment.canUseDOM && 'documentMode' in document) { documentMode = document.documentMode; } // Webkit offers a very useful `textInput` event that can be used to // directly represent `beforeInput`. The IE `textinput` event is not as // useful, so we don't use it. var canUseTextInputEvent = ExecutionEnvironment.canUseDOM && 'TextEvent' in window && !documentMode && !isPresto(); // In IE9+, we have access to composition events, but the data supplied // by the native compositionend event may be incorrect. Japanese ideographic // spaces, for instance (\u3000) are not recorded correctly. var useFallbackCompositionData = ExecutionEnvironment.canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11); /** * Opera <= 12 includes TextEvent in window, but does not fire * text input events. Rely on keypress instead. */ function isPresto() { var opera = window.opera; return typeof opera === 'object' && typeof opera.version === 'function' && parseInt(opera.version(), 10) <= 12; } var SPACEBAR_CODE = 32; var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE); // Events and their corresponding property names. var eventTypes = { beforeInput: { phasedRegistrationNames: { bubbled: 'onBeforeInput', captured: 'onBeforeInputCapture' }, dependencies: ['topCompositionEnd', 'topKeyPress', 'topTextInput', 'topPaste'] }, compositionEnd: { phasedRegistrationNames: { bubbled: 'onCompositionEnd', captured: 'onCompositionEndCapture' }, dependencies: ['topBlur', 'topCompositionEnd', 'topKeyDown', 'topKeyPress', 'topKeyUp', 'topMouseDown'] }, compositionStart: { phasedRegistrationNames: { bubbled: 'onCompositionStart', captured: 'onCompositionStartCapture' }, dependencies: ['topBlur', 'topCompositionStart', 'topKeyDown', 'topKeyPress', 'topKeyUp', 'topMouseDown'] }, compositionUpdate: { phasedRegistrationNames: { bubbled: 'onCompositionUpdate', captured: 'onCompositionUpdateCapture' }, dependencies: ['topBlur', 'topCompositionUpdate', 'topKeyDown', 'topKeyPress', 'topKeyUp', 'topMouseDown'] } }; // Track whether we've ever handled a keypress on the space key. var hasSpaceKeypress = false; /** * Return whether a native keypress event is assumed to be a command. * This is required because Firefox fires `keypress` events for key commands * (cut, copy, select-all, etc.) even though no character is inserted. */ function isKeypressCommand(nativeEvent) { return (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && // ctrlKey && altKey is equivalent to AltGr, and is not a command. !(nativeEvent.ctrlKey && nativeEvent.altKey); } /** * Translate native top level events into event types. * * @param {string} topLevelType * @return {object} */ function getCompositionEventType(topLevelType) { switch (topLevelType) { case 'topCompositionStart': return eventTypes.compositionStart; case 'topCompositionEnd': return eventTypes.compositionEnd; case 'topCompositionUpdate': return eventTypes.compositionUpdate; } } /** * Does our fallback best-guess model think this event signifies that * composition has begun? * * @param {string} topLevelType * @param {object} nativeEvent * @return {boolean} */ function isFallbackCompositionStart(topLevelType, nativeEvent) { return topLevelType === 'topKeyDown' && nativeEvent.keyCode === START_KEYCODE; } /** * Does our fallback mode think that this event is the end of composition? * * @param {string} topLevelType * @param {object} nativeEvent * @return {boolean} */ function isFallbackCompositionEnd(topLevelType, nativeEvent) { switch (topLevelType) { case 'topKeyUp': // Command keys insert or clear IME input. return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1; case 'topKeyDown': // Expect IME keyCode on each keydown. If we get any other // code we must have exited earlier. return nativeEvent.keyCode !== START_KEYCODE; case 'topKeyPress': case 'topMouseDown': case 'topBlur': // Events are not possible without cancelling IME. return true; default: return false; } } /** * Google Input Tools provides composition data via a CustomEvent, * with the `data` property populated in the `detail` object. If this * is available on the event object, use it. If not, this is a plain * composition event and we have nothing special to extract. * * @param {object} nativeEvent * @return {?string} */ function getDataFromCustomEvent(nativeEvent) { var detail = nativeEvent.detail; if (typeof detail === 'object' && 'data' in detail) { return detail.data; } return null; } // Track the current IME composition status, if any. var isComposing = false; /** * @return {?object} A SyntheticCompositionEvent. */ function extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) { var eventType; var fallbackData; if (canUseCompositionEvent) { eventType = getCompositionEventType(topLevelType); } else if (!isComposing) { if (isFallbackCompositionStart(topLevelType, nativeEvent)) { eventType = eventTypes.compositionStart; } } else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) { eventType = eventTypes.compositionEnd; } if (!eventType) { return null; } if (useFallbackCompositionData) { // The current composition is stored statically and must not be // overwritten while composition continues. if (!isComposing && eventType === eventTypes.compositionStart) { isComposing = initialize(nativeEventTarget); } else if (eventType === eventTypes.compositionEnd) { if (isComposing) { fallbackData = getData(); } } } var event = SyntheticCompositionEvent.getPooled(eventType, targetInst, nativeEvent, nativeEventTarget); if (fallbackData) { // Inject data generated from fallback path into the synthetic event. // This matches the property of native CompositionEventInterface. event.data = fallbackData; } else { var customData = getDataFromCustomEvent(nativeEvent); if (customData !== null) { event.data = customData; } } accumulateTwoPhaseDispatches(event); return event; } /** * @param {TopLevelTypes} topLevelType Record from `BrowserEventConstants`. * @param {object} nativeEvent Native browser event. * @return {?string} The string corresponding to this `beforeInput` event. */ function getNativeBeforeInputChars(topLevelType, nativeEvent) { switch (topLevelType) { case 'topCompositionEnd': return getDataFromCustomEvent(nativeEvent); case 'topKeyPress': /** * If native `textInput` events are available, our goal is to make * use of them. However, there is a special case: the spacebar key. * In Webkit, preventing default on a spacebar `textInput` event * cancels character insertion, but it *also* causes the browser * to fall back to its default spacebar behavior of scrolling the * page. * * Tracking at: * https://code.google.com/p/chromium/issues/detail?id=355103 * * To avoid this issue, use the keypress event as if no `textInput` * event is available. */ var which = nativeEvent.which; if (which !== SPACEBAR_CODE) { return null; } hasSpaceKeypress = true; return SPACEBAR_CHAR; case 'topTextInput': // Record the characters to be added to the DOM. var chars = nativeEvent.data; // If it's a spacebar character, assume that we have already handled // it at the keypress level and bail immediately. Android Chrome // doesn't give us keycodes, so we need to blacklist it. if (chars === SPACEBAR_CHAR && hasSpaceKeypress) { return null; } return chars; default: // For other native event types, do nothing. return null; } } /** * For browsers that do not provide the `textInput` event, extract the * appropriate string to use for SyntheticInputEvent. * * @param {string} topLevelType Record from `BrowserEventConstants`. * @param {object} nativeEvent Native browser event. * @return {?string} The fallback string for this `beforeInput` event. */ function getFallbackBeforeInputChars(topLevelType, nativeEvent) { // If we are currently composing (IME) and using a fallback to do so, // try to extract the composed characters from the fallback object. // If composition event is available, we extract a string only at // compositionevent, otherwise extract it at fallback events. if (isComposing) { if (topLevelType === 'topCompositionEnd' || !canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent)) { var chars = getData(); reset(); isComposing = false; return chars; } return null; } switch (topLevelType) { case 'topPaste': // If a paste event occurs after a keypress, throw out the input // chars. Paste events should not lead to BeforeInput events. return null; case 'topKeyPress': /** * As of v27, Firefox may fire keypress events even when no character * will be inserted. A few possibilities: * * - `which` is `0`. Arrow keys, Esc key, etc. * * - `which` is the pressed key code, but no char is available. * Ex: 'AltGr + d` in Polish. There is no modified character for * this key combination and no character is inserted into the * document, but FF fires the keypress for char code `100` anyway. * No `input` event will occur. * * - `which` is the pressed key code, but a command combination is * being used. Ex: `Cmd+C`. No character is inserted, and no * `input` event will occur. */ if (!isKeypressCommand(nativeEvent)) { // IE fires the `keypress` event when a user types an emoji via // Touch keyboard of Windows. In such a case, the `char` property // holds an emoji character like `\uD83D\uDE0A`. Because its length // is 2, the property `which` does not represent an emoji correctly. // In such a case, we directly return the `char` property instead of // using `which`. if (nativeEvent.char && nativeEvent.char.length > 1) { return nativeEvent.char; } else if (nativeEvent.which) { return String.fromCharCode(nativeEvent.which); } } return null; case 'topCompositionEnd': return useFallbackCompositionData ? null : nativeEvent.data; default: return null; } } /** * Extract a SyntheticInputEvent for `beforeInput`, based on either native * `textInput` or fallback behavior. * * @return {?object} A SyntheticInputEvent. */ function extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) { var chars; if (canUseTextInputEvent) { chars = getNativeBeforeInputChars(topLevelType, nativeEvent); } else { chars = getFallbackBeforeInputChars(topLevelType, nativeEvent); } // If no characters are being inserted, no BeforeInput event should // be fired. if (!chars) { return null; } var event = SyntheticInputEvent.getPooled(eventTypes.beforeInput, targetInst, nativeEvent, nativeEventTarget); event.data = chars; accumulateTwoPhaseDispatches(event); return event; } /** * Create an `onBeforeInput` event to match * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents. * * This event plugin is based on the native `textInput` event * available in Chrome, Safari, Opera, and IE. This event fires after * `onKeyPress` and `onCompositionEnd`, but before `onInput`. * * `beforeInput` is spec'd but not implemented in any browsers, and * the `input` event does not provide any useful information about what has * actually been added, contrary to the spec. Thus, `textInput` is the best * available event to identify the characters that have actually been inserted * into the target node. * * This plugin is also responsible for emitting `composition` events, thus * allowing us to share composition fallback code for both `beforeInput` and * `composition` event types. */ var BeforeInputEventPlugin = { eventTypes: eventTypes, extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) { return [extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget), extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget)]; } }; // Use to restore controlled state after a change event has fired. var fiberHostComponent = null; var ReactControlledComponentInjection = { injectFiberControlledHostComponent: function (hostComponentImpl) { // The fiber implementation doesn't use dynamic dispatch so we need to // inject the implementation. fiberHostComponent = hostComponentImpl; } }; var restoreTarget = null; var restoreQueue = null; function restoreStateOfTarget(target) { // We perform this translation at the end of the event loop so that we // always receive the correct fiber here var internalInstance = getInstanceFromNode(target); if (!internalInstance) { // Unmounted return; } !(fiberHostComponent && typeof fiberHostComponent.restoreControlledState === 'function') ? invariant(false, 'Fiber needs to be injected to handle a fiber target for controlled events. This error is likely caused by a bug in React. Please file an issue.') : void 0; var props = getFiberCurrentPropsFromNode(internalInstance.stateNode); fiberHostComponent.restoreControlledState(internalInstance.stateNode, internalInstance.type, props); } var injection$3 = ReactControlledComponentInjection; function enqueueStateRestore(target) { if (restoreTarget) { if (restoreQueue) { restoreQueue.push(target); } else { restoreQueue = [target]; } } else { restoreTarget = target; } } function restoreStateIfNeeded() { if (!restoreTarget) { return; } var target = restoreTarget; var queuedTargets = restoreQueue; restoreTarget = null; restoreQueue = null; restoreStateOfTarget(target); if (queuedTargets) { for (var i = 0; i < queuedTargets.length; i++) { restoreStateOfTarget(queuedTargets[i]); } } } var ReactControlledComponent = Object.freeze({ injection: injection$3, enqueueStateRestore: enqueueStateRestore, restoreStateIfNeeded: restoreStateIfNeeded }); // Used as a way to call batchedUpdates when we don't have a reference to // the renderer. Such as when we're dispatching events or if third party // libraries need to call batchedUpdates. Eventually, this API will go away when // everything is batched by default. We'll then have a similar API to opt-out of // scheduled work and instead do synchronous work. // Defaults var fiberBatchedUpdates = function (fn, bookkeeping) { return fn(bookkeeping); }; var isNestingBatched = false; function batchedUpdates(fn, bookkeeping) { if (isNestingBatched) { // If we are currently inside another batch, we need to wait until it // fully completes before restoring state. Therefore, we add the target to // a queue of work. return fiberBatchedUpdates(fn, bookkeeping); } isNestingBatched = true; try { return fiberBatchedUpdates(fn, bookkeeping); } finally { // Here we wait until all updates have propagated, which is important // when using controlled components within layers: // https://github.com/facebook/react/issues/1698 // Then we restore state of any controlled component. isNestingBatched = false; restoreStateIfNeeded(); } } var ReactGenericBatchingInjection = { injectFiberBatchedUpdates: function (_batchedUpdates) { fiberBatchedUpdates = _batchedUpdates; } }; var injection$4 = ReactGenericBatchingInjection; /** * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary */ var supportedInputTypes = { color: true, date: true, datetime: true, 'datetime-local': true, email: true, month: true, number: true, password: true, range: true, search: true, tel: true, text: true, time: true, url: true, week: true }; function isTextInputElement(elem) { var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase(); if (nodeName === 'input') { return !!supportedInputTypes[elem.type]; } if (nodeName === 'textarea') { return true; } return false; } /** * HTML nodeType values that represent the type of the node */ var ELEMENT_NODE = 1; var TEXT_NODE = 3; var COMMENT_NODE = 8; var DOCUMENT_NODE = 9; var DOCUMENT_FRAGMENT_NODE = 11; /** * Gets the target node from a native browser event by accounting for * inconsistencies in browser DOM APIs. * * @param {object} nativeEvent Native browser event. * @return {DOMEventTarget} Target node. */ function getEventTarget(nativeEvent) { var target = nativeEvent.target || nativeEvent.srcElement || window; // Normalize SVG element events #4963 if (target.correspondingUseElement) { target = target.correspondingUseElement; } // Safari may fire events on text nodes (Node.TEXT_NODE is 3). // @see http://www.quirksmode.org/js/events_properties.html return target.nodeType === TEXT_NODE ? target.parentNode : target; } var useHasFeature; if (ExecutionEnvironment.canUseDOM) { useHasFeature = document.implementation && document.implementation.hasFeature && // always returns true in newer browsers as per the standard. // @see http://dom.spec.whatwg.org/#dom-domimplementation-hasfeature document.implementation.hasFeature('', '') !== true; } /** * Checks if an event is supported in the current execution environment. * * NOTE: This will not work correctly for non-generic events such as `change`, * `reset`, `load`, `error`, and `select`. * * Borrows from Modernizr. * * @param {string} eventNameSuffix Event name, e.g. "click". * @param {?boolean} capture Check if the capture phase is supported. * @return {boolean} True if the event is supported. * @internal * @license Modernizr 3.0.0pre (Custom Build) | MIT */ function isEventSupported(eventNameSuffix, capture) { if (!ExecutionEnvironment.canUseDOM || capture && !('addEventListener' in document)) { return false; } var eventName = 'on' + eventNameSuffix; var isSupported = eventName in document; if (!isSupported) { var element = document.createElement('div'); element.setAttribute(eventName, 'return;'); isSupported = typeof element[eventName] === 'function'; } if (!isSupported && useHasFeature && eventNameSuffix === 'wheel') { // This is the only way to test support for the `wheel` event in IE9+. isSupported = document.implementation.hasFeature('Events.wheel', '3.0'); } return isSupported; } function isCheckable(elem) { var type = elem.type; var nodeName = elem.nodeName; return nodeName && nodeName.toLowerCase() === 'input' && (type === 'checkbox' || type === 'radio'); } function getTracker(node) { return node._valueTracker; } function detachTracker(node) { node._valueTracker = null; } function getValueFromNode(node) { var value = ''; if (!node) { return value; } if (isCheckable(node)) { value = node.checked ? 'true' : 'false'; } else { value = node.value; } return value; } function trackValueOnNode(node) { var valueField = isCheckable(node) ? 'checked' : 'value'; var descriptor = Object.getOwnPropertyDescriptor(node.constructor.prototype, valueField); var currentValue = '' + node[valueField]; // if someone has already defined a value or Safari, then bail // and don't track value will cause over reporting of changes, // but it's better then a hard failure // (needed for certain tests that spyOn input values and Safari) if (node.hasOwnProperty(valueField) || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') { return; } Object.defineProperty(node, valueField, { enumerable: descriptor.enumerable, configurable: true, get: function () { return descriptor.get.call(this); }, set: function (value) { currentValue = '' + value; descriptor.set.call(this, value); } }); var tracker = { getValue: function () { return currentValue; }, setValue: function (value) { currentValue = '' + value; }, stopTracking: function () { detachTracker(node); delete node[valueField]; } }; return tracker; } function track(node) { if (getTracker(node)) { return; } // TODO: Once it's just Fiber we can move this to node._wrapperState node._valueTracker = trackValueOnNode(node); } function updateValueIfChanged(node) { if (!node) { return false; } var tracker = getTracker(node); // if there is no tracker at this point it's unlikely // that trying again will succeed if (!tracker) { return true; } var lastValue = tracker.getValue(); var nextValue = getValueFromNode(node); if (nextValue !== lastValue) { tracker.setValue(nextValue); return true; } return false; } var eventTypes$1 = { change: { phasedRegistrationNames: { bubbled: 'onChange', captured: 'onChangeCapture' }, dependencies: ['topBlur', 'topChange', 'topClick', 'topFocus', 'topInput', 'topKeyDown', 'topKeyUp', 'topSelectionChange'] } }; function createAndAccumulateChangeEvent(inst, nativeEvent, target) { var event = SyntheticEvent$1.getPooled(eventTypes$1.change, inst, nativeEvent, target); event.type = 'change'; // Flag this event loop as needing state restore. enqueueStateRestore(target); accumulateTwoPhaseDispatches(event); return event; } /** * For IE shims */ var activeElement = null; var activeElementInst = null; /** * SECTION: handle `change` event */ function shouldUseChangeEvent(elem) { var nodeName = elem.nodeName && elem.nodeName.toLowerCase(); return nodeName === 'select' || nodeName === 'input' && elem.type === 'file'; } function manualDispatchChangeEvent(nativeEvent) { var event = createAndAccumulateChangeEvent(activeElementInst, nativeEvent, getEventTarget(nativeEvent)); // If change and propertychange bubbled, we'd just bind to it like all the // other events and have it go through ReactBrowserEventEmitter. Since it // doesn't, we manually listen for the events and so we have to enqueue and // process the abstract event manually. // // Batching is necessary here in order to ensure that all event handlers run // before the next rerender (including event handlers attached to ancestor // elements instead of directly on the input). Without this, controlled // components don't work properly in conjunction with event bubbling because // the component is rerendered and the value reverted before all the event // handlers can run. See https://github.com/facebook/react/issues/708. batchedUpdates(runEventInBatch, event); } function runEventInBatch(event) { enqueueEvents(event); processEventQueue(false); } function getInstIfValueChanged(targetInst) { var targetNode = getNodeFromInstance$1(targetInst); if (updateValueIfChanged(targetNode)) { return targetInst; } } function getTargetInstForChangeEvent(topLevelType, targetInst) { if (topLevelType === 'topChange') { return targetInst; } } /** * SECTION: handle `input` event */ var isInputEventSupported = false; if (ExecutionEnvironment.canUseDOM) { // IE9 claims to support the input event but fails to trigger it when // deleting text, so we ignore its input events. isInputEventSupported = isEventSupported('input') && (!document.documentMode || document.documentMode > 9); } /** * (For IE <=9) Starts tracking propertychange events on the passed-in element * and override the value property so that we can distinguish user events from * value changes in JS. */ function startWatchingForValueChange(target, targetInst) { activeElement = target; activeElementInst = targetInst; activeElement.attachEvent('onpropertychange', handlePropertyChange); } /** * (For IE <=9) Removes the event listeners from the currently-tracked element, * if any exists. */ function stopWatchingForValueChange() { if (!activeElement) { return; } activeElement.detachEvent('onpropertychange', handlePropertyChange); activeElement = null; activeElementInst = null; } /** * (For IE <=9) Handles a propertychange event, sending a `change` event if * the value of the active element has changed. */ function handlePropertyChange(nativeEvent) { if (nativeEvent.propertyName !== 'value') { return; } if (getInstIfValueChanged(activeElementInst)) { manualDispatchChangeEvent(nativeEvent); } } function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) { if (topLevelType === 'topFocus') { // In IE9, propertychange fires for most input events but is buggy and // doesn't fire when text is deleted, but conveniently, selectionchange // appears to fire in all of the remaining cases so we catch those and // forward the event if the value has changed // In either case, we don't want to call the event handler if the value // is changed from JS so we redefine a setter for `.value` that updates // our activeElementValue variable, allowing us to ignore those changes // // stopWatching() should be a noop here but we call it just in case we // missed a blur event somehow. stopWatchingForValueChange(); startWatchingForValueChange(target, targetInst); } else if (topLevelType === 'topBlur') { stopWatchingForValueChange(); } } // For IE8 and IE9. function getTargetInstForInputEventPolyfill(topLevelType, targetInst) { if (topLevelType === 'topSelectionChange' || topLevelType === 'topKeyUp' || topLevelType === 'topKeyDown') { // On the selectionchange event, the target is just document which isn't // helpful for us so just check activeElement instead. // // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire // propertychange on the first input event after setting `value` from a // script and fires only keydown, keypress, keyup. Catching keyup usually // gets it and catching keydown lets us fire an event for the first // keystroke if user does a key repeat (it'll be a little delayed: right // before the second keystroke). Other input methods (e.g., paste) seem to // fire selectionchange normally. return getInstIfValueChanged(activeElementInst); } } /** * SECTION: handle `click` event */ function shouldUseClickEvent(elem) { // Use the `click` event to detect changes to checkbox and radio inputs. // This approach works across all browsers, whereas `change` does not fire // until `blur` in IE8. var nodeName = elem.nodeName; return nodeName && nodeName.toLowerCase() === 'input' && (elem.type === 'checkbox' || elem.type === 'radio'); } function getTargetInstForClickEvent(topLevelType, targetInst) { if (topLevelType === 'topClick') { return getInstIfValueChanged(targetInst); } } function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) { if (topLevelType === 'topInput' || topLevelType === 'topChange') { return getInstIfValueChanged(targetInst); } } function handleControlledInputBlur(inst, node) { // TODO: In IE, inst is occasionally null. Why? if (inst == null) { return; } // Fiber and ReactDOM keep wrapper state in separate places var state = inst._wrapperState || node._wrapperState; if (!state || !state.controlled || node.type !== 'number') { return; } // If controlled, assign the value attribute to the current value on blur var value = '' + node.value; if (node.getAttribute('value') !== value) { node.setAttribute('value', value); } } /** * This plugin creates an `onChange` event that normalizes change events * across form elements. This event fires at a time when it's possible to * change the element's value without seeing a flicker. * * Supported elements are: * - input (see `isTextInputElement`) * - textarea * - select */ var ChangeEventPlugin = { eventTypes: eventTypes$1, _isInputEventSupported: isInputEventSupported, extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) { var targetNode = targetInst ? getNodeFromInstance$1(targetInst) : window; var getTargetInstFunc, handleEventFunc; if (shouldUseChangeEvent(targetNode)) { getTargetInstFunc = getTargetInstForChangeEvent; } else if (isTextInputElement(targetNode)) { if (isInputEventSupported) { getTargetInstFunc = getTargetInstForInputOrChangeEvent; } else { getTargetInstFunc = getTargetInstForInputEventPolyfill; handleEventFunc = handleEventsForInputEventPolyfill; } } else if (shouldUseClickEvent(targetNode)) { getTargetInstFunc = getTargetInstForClickEvent; } if (getTargetInstFunc) { var inst = getTargetInstFunc(topLevelType, targetInst); if (inst) { var event = createAndAccumulateChangeEvent(inst, nativeEvent, nativeEventTarget); return event; } } if (handleEventFunc) { handleEventFunc(topLevelType, targetNode, targetInst); } // When blurring, set the value attribute for number inputs if (topLevelType === 'topBlur') { handleControlledInputBlur(targetInst, targetNode); } } }; /** * Module that is injectable into `EventPluginHub`, that specifies a * deterministic ordering of `EventPlugin`s. A convenient way to reason about * plugins, without having to package every one of them. This is better than * having plugins be ordered in the same order that they are injected because * that ordering would be influenced by the packaging order. * `ResponderEventPlugin` must occur before `SimpleEventPlugin` so that * preventing default on events is convenient in `SimpleEventPlugin` handlers. */ var DOMEventPluginOrder = ['ResponderEventPlugin', 'SimpleEventPlugin', 'TapEventPlugin', 'EnterLeaveEventPlugin', 'ChangeEventPlugin', 'SelectEventPlugin', 'BeforeInputEventPlugin']; /** * @interface UIEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ var UIEventInterface = { view: null, detail: null }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticEvent} */ function SyntheticUIEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticEvent$1.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticEvent$1.augmentClass(SyntheticUIEvent, UIEventInterface); /** * Translation from modifier key to the associated property in the event. * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers */ var modifierKeyToProp = { Alt: 'altKey', Control: 'ctrlKey', Meta: 'metaKey', Shift: 'shiftKey' }; // IE8 does not implement getModifierState so we simply map it to the only // modifier keys exposed by the event itself, does not support Lock-keys. // Currently, all major browsers except Chrome seems to support Lock-keys. function modifierStateGetter(keyArg) { var syntheticEvent = this; var nativeEvent = syntheticEvent.nativeEvent; if (nativeEvent.getModifierState) { return nativeEvent.getModifierState(keyArg); } var keyProp = modifierKeyToProp[keyArg]; return keyProp ? !!nativeEvent[keyProp] : false; } function getEventModifierState(nativeEvent) { return modifierStateGetter; } /** * @interface MouseEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ var MouseEventInterface = { screenX: null, screenY: null, clientX: null, clientY: null, pageX: null, pageY: null, ctrlKey: null, shiftKey: null, altKey: null, metaKey: null, getModifierState: getEventModifierState, button: null, buttons: null, relatedTarget: function (event) { return event.relatedTarget || (event.fromElement === event.srcElement ? event.toElement : event.fromElement); } }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticUIEvent} */ function SyntheticMouseEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticUIEvent.augmentClass(SyntheticMouseEvent, MouseEventInterface); var eventTypes$2 = { mouseEnter: { registrationName: 'onMouseEnter', dependencies: ['topMouseOut', 'topMouseOver'] }, mouseLeave: { registrationName: 'onMouseLeave', dependencies: ['topMouseOut', 'topMouseOver'] } }; var EnterLeaveEventPlugin = { eventTypes: eventTypes$2, /** * For almost every interaction we care about, there will be both a top-level * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that * we do not extract duplicate events. However, moving the mouse into the * browser from outside will not fire a `mouseout` event. In this case, we use * the `mouseover` top-level event. */ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) { if (topLevelType === 'topMouseOver' && (nativeEvent.relatedTarget || nativeEvent.fromElement)) { return null; } if (topLevelType !== 'topMouseOut' && topLevelType !== 'topMouseOver') { // Must not be a mouse in or mouse out - ignoring. return null; } var win; if (nativeEventTarget.window === nativeEventTarget) { // `nativeEventTarget` is probably a window object. win = nativeEventTarget; } else { // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8. var doc = nativeEventTarget.ownerDocument; if (doc) { win = doc.defaultView || doc.parentWindow; } else { win = window; } } var from; var to; if (topLevelType === 'topMouseOut') { from = targetInst; var related = nativeEvent.relatedTarget || nativeEvent.toElement; to = related ? getClosestInstanceFromNode(related) : null; } else { // Moving to a node from outside the window. from = null; to = targetInst; } if (from === to) { // Nothing pertains to our managed components. return null; } var fromNode = from == null ? win : getNodeFromInstance$1(from); var toNode = to == null ? win : getNodeFromInstance$1(to); var leave = SyntheticMouseEvent.getPooled(eventTypes$2.mouseLeave, from, nativeEvent, nativeEventTarget); leave.type = 'mouseleave'; leave.target = fromNode; leave.relatedTarget = toNode; var enter = SyntheticMouseEvent.getPooled(eventTypes$2.mouseEnter, to, nativeEvent, nativeEventTarget); enter.type = 'mouseenter'; enter.target = toNode; enter.relatedTarget = fromNode; accumulateEnterLeaveDispatches(leave, enter, from, to); return [leave, enter]; } }; /** * `ReactInstanceMap` maintains a mapping from a public facing stateful * instance (key) and the internal representation (value). This allows public * methods to accept the user facing instance as an argument and map them back * to internal methods. * * Note that this module is currently shared and assumed to be stateless. * If this becomes an actual Map, that will break. */ /** * This API should be called `delete` but we'd have to make sure to always * transform these to strings for IE support. When this transform is fully * supported we can rename it. */ function get(key) { return key._reactInternalFiber; } function has(key) { return key._reactInternalFiber !== undefined; } function set(key, value) { key._reactInternalFiber = value; } var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; var ReactCurrentOwner = ReactInternals.ReactCurrentOwner; var ReactDebugCurrentFrame = ReactInternals.ReactDebugCurrentFrame; function getComponentName(fiber) { var type = fiber.type; if (typeof type === 'string') { return type; } if (typeof type === 'function') { return type.displayName || type.name; } return null; } // Don't change these two values: var NoEffect = 0; // 0b00000000 var PerformedWork = 1; // 0b00000001 // You can change the rest (and add more). var Placement = 2; // 0b00000010 var Update = 4; // 0b00000100 var PlacementAndUpdate = 6; // 0b00000110 var Deletion = 8; // 0b00001000 var ContentReset = 16; // 0b00010000 var Callback = 32; // 0b00100000 var Err = 64; // 0b01000000 var Ref = 128; // 0b10000000 var MOUNTING = 1; var MOUNTED = 2; var UNMOUNTED = 3; function isFiberMountedImpl(fiber) { var node = fiber; if (!fiber.alternate) { // If there is no alternate, this might be a new tree that isn't inserted // yet. If it is, then it will have a pending insertion effect on it. if ((node.effectTag & Placement) !== NoEffect) { return MOUNTING; } while (node['return']) { node = node['return']; if ((node.effectTag & Placement) !== NoEffect) { return MOUNTING; } } } else { while (node['return']) { node = node['return']; } } if (node.tag === HostRoot) { // TODO: Check if this was a nested HostRoot when used with // renderContainerIntoSubtree. return MOUNTED; } // If we didn't hit the root, that means that we're in an disconnected tree // that has been unmounted. return UNMOUNTED; } function isFiberMounted(fiber) { return isFiberMountedImpl(fiber) === MOUNTED; } function isMounted(component) { { var owner = ReactCurrentOwner.current; if (owner !== null && owner.tag === ClassComponent) { var ownerFiber = owner; var instance = ownerFiber.stateNode; warning(instance._warnedAboutRefsInRender, '%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', getComponentName(ownerFiber) || 'A component'); instance._warnedAboutRefsInRender = true; } } var fiber = get(component); if (!fiber) { return false; } return isFiberMountedImpl(fiber) === MOUNTED; } function assertIsMounted(fiber) { !(isFiberMountedImpl(fiber) === MOUNTED) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0; } function findCurrentFiberUsingSlowPath(fiber) { var alternate = fiber.alternate; if (!alternate) { // If there is no alternate, then we only need to check if it is mounted. var state = isFiberMountedImpl(fiber); !(state !== UNMOUNTED) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0; if (state === MOUNTING) { return null; } return fiber; } // If we have two possible branches, we'll walk backwards up to the root // to see what path the root points to. On the way we may hit one of the // special cases and we'll deal with them. var a = fiber; var b = alternate; while (true) { var parentA = a['return']; var parentB = parentA ? parentA.alternate : null; if (!parentA || !parentB) { // We're at the root. break; } // If both copies of the parent fiber point to the same child, we can // assume that the child is current. This happens when we bailout on low // priority: the bailed out fiber's child reuses the current child. if (parentA.child === parentB.child) { var child = parentA.child; while (child) { if (child === a) { // We've determined that A is the current branch. assertIsMounted(parentA); return fiber; } if (child === b) { // We've determined that B is the current branch. assertIsMounted(parentA); return alternate; } child = child.sibling; } // We should never have an alternate for any mounting node. So the only // way this could possibly happen is if this was unmounted, if at all. invariant(false, 'Unable to find node on an unmounted component.'); } if (a['return'] !== b['return']) { // The return pointer of A and the return pointer of B point to different // fibers. We assume that return pointers never criss-cross, so A must // belong to the child set of A.return, and B must belong to the child // set of B.return. a = parentA; b = parentB; } else { // The return pointers point to the same fiber. We'll have to use the // default, slow path: scan the child sets of each parent alternate to see // which child belongs to which set. // // Search parent A's child set var didFindChild = false; var _child = parentA.child; while (_child) { if (_child === a) { didFindChild = true; a = parentA; b = parentB; break; } if (_child === b) { didFindChild = true; b = parentA; a = parentB; break; } _child = _child.sibling; } if (!didFindChild) { // Search parent B's child set _child = parentB.child; while (_child) { if (_child === a) { didFindChild = true; a = parentB; b = parentA; break; } if (_child === b) { didFindChild = true; b = parentB; a = parentA; break; } _child = _child.sibling; } !didFindChild ? invariant(false, 'Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue.') : void 0; } } !(a.alternate === b) ? invariant(false, 'Return fibers should always be each others\' alternates. This error is likely caused by a bug in React. Please file an issue.') : void 0; } // If the root is not a host container, we're in a disconnected tree. I.e. // unmounted. !(a.tag === HostRoot) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0; if (a.stateNode.current === a) { // We've determined that A is the current branch. return fiber; } // Otherwise B has to be current branch. return alternate; } function findCurrentHostFiber(parent) { var currentParent = findCurrentFiberUsingSlowPath(parent); if (!currentParent) { return null; } // Next we'll drill down this component to find the first HostComponent/Text. var node = currentParent; while (true) { if (node.tag === HostComponent || node.tag === HostText) { return node; } else if (node.child) { node.child['return'] = node; node = node.child; continue; } if (node === currentParent) { return null; } while (!node.sibling) { if (!node['return'] || node['return'] === currentParent) { return null; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; } // Flow needs the return null here, but ESLint complains about it. // eslint-disable-next-line no-unreachable return null; } function findCurrentHostFiberWithNoPortals(parent) { var currentParent = findCurrentFiberUsingSlowPath(parent); if (!currentParent) { return null; } // Next we'll drill down this component to find the first HostComponent/Text. var node = currentParent; while (true) { if (node.tag === HostComponent || node.tag === HostText) { return node; } else if (node.child && node.tag !== HostPortal) { node.child['return'] = node; node = node.child; continue; } if (node === currentParent) { return null; } while (!node.sibling) { if (!node['return'] || node['return'] === currentParent) { return null; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; } // Flow needs the return null here, but ESLint complains about it. // eslint-disable-next-line no-unreachable return null; } var CALLBACK_BOOKKEEPING_POOL_SIZE = 10; var callbackBookkeepingPool = []; /** * Find the deepest React component completely containing the root of the * passed-in instance (for use when entire React trees are nested within each * other). If React trees are not nested, returns null. */ function findRootContainerNode(inst) { // 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. while (inst['return']) { inst = inst['return']; } if (inst.tag !== HostRoot) { // This can happen if we're in a detached tree. return null; } return inst.stateNode.containerInfo; } // Used to store ancestor hierarchy in top level callback function getTopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst) { if (callbackBookkeepingPool.length) { var instance = callbackBookkeepingPool.pop(); instance.topLevelType = topLevelType; instance.nativeEvent = nativeEvent; instance.targetInst = targetInst; return instance; } return { topLevelType: topLevelType, nativeEvent: nativeEvent, targetInst: targetInst, ancestors: [] }; } function releaseTopLevelCallbackBookKeeping(instance) { instance.topLevelType = null; instance.nativeEvent = null; instance.targetInst = null; instance.ancestors.length = 0; if (callbackBookkeepingPool.length < CALLBACK_BOOKKEEPING_POOL_SIZE) { callbackBookkeepingPool.push(instance); } } function handleTopLevelImpl(bookKeeping) { var targetInst = bookKeeping.targetInst; // Loop through the hierarchy, in case there's any nested components. // It's important that we build the array of ancestors before calling any // event handlers, because event handlers can modify the DOM, leading to // inconsistencies with ReactMount's node cache. See #1105. var ancestor = targetInst; do { if (!ancestor) { bookKeeping.ancestors.push(ancestor); break; } var root = findRootContainerNode(ancestor); if (!root) { break; } bookKeeping.ancestors.push(ancestor); ancestor = getClosestInstanceFromNode(root); } while (ancestor); for (var i = 0; i < bookKeeping.ancestors.length; i++) { targetInst = bookKeeping.ancestors[i]; _handleTopLevel(bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent)); } } // TODO: can we stop exporting these? var _enabled = true; var _handleTopLevel = void 0; function setHandleTopLevel(handleTopLevel) { _handleTopLevel = handleTopLevel; } function setEnabled(enabled) { _enabled = !!enabled; } function isEnabled() { return _enabled; } /** * Traps top-level events by using event bubbling. * * @param {string} topLevelType Record from `BrowserEventConstants`. * @param {string} handlerBaseName Event name (e.g. "click"). * @param {object} element Element on which to attach listener. * @return {?object} An object with a remove function which will forcefully * remove the listener. * @internal */ function trapBubbledEvent(topLevelType, handlerBaseName, element) { if (!element) { return null; } return EventListener.listen(element, handlerBaseName, dispatchEvent.bind(null, topLevelType)); } /** * Traps a top-level event by using event capturing. * * @param {string} topLevelType Record from `BrowserEventConstants`. * @param {string} handlerBaseName Event name (e.g. "click"). * @param {object} element Element on which to attach listener. * @return {?object} An object with a remove function which will forcefully * remove the listener. * @internal */ function trapCapturedEvent(topLevelType, handlerBaseName, element) { if (!element) { return null; } return EventListener.capture(element, handlerBaseName, dispatchEvent.bind(null, topLevelType)); } function dispatchEvent(topLevelType, nativeEvent) { if (!_enabled) { return; } var nativeEventTarget = getEventTarget(nativeEvent); var targetInst = getClosestInstanceFromNode(nativeEventTarget); if (targetInst !== null && typeof targetInst.tag === 'number' && !isFiberMounted(targetInst)) { // If we get an event (ex: img onload) before committing that // component's mount, ignore it for now (that is, treat it as if it was an // event on a non-React tree). We might also consider queueing events and // dispatching them after the mount. targetInst = null; } var bookKeeping = getTopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst); try { // Event queue being processed in the same cycle allows // `preventDefault`. batchedUpdates(handleTopLevelImpl, bookKeeping); } finally { releaseTopLevelCallbackBookKeeping(bookKeeping); } } var ReactDOMEventListener = Object.freeze({ get _enabled () { return _enabled; }, get _handleTopLevel () { return _handleTopLevel; }, setHandleTopLevel: setHandleTopLevel, setEnabled: setEnabled, isEnabled: isEnabled, trapBubbledEvent: trapBubbledEvent, trapCapturedEvent: trapCapturedEvent, dispatchEvent: dispatchEvent }); /** * Generate a mapping of standard vendor prefixes using the defined style property and event name. * * @param {string} styleProp * @param {string} eventName * @returns {object} */ function makePrefixMap(styleProp, eventName) { var prefixes = {}; prefixes[styleProp.toLowerCase()] = eventName.toLowerCase(); prefixes['Webkit' + styleProp] = 'webkit' + eventName; prefixes['Moz' + styleProp] = 'moz' + eventName; prefixes['ms' + styleProp] = 'MS' + eventName; prefixes['O' + styleProp] = 'o' + eventName.toLowerCase(); return prefixes; } /** * A list of event names to a configurable list of vendor prefixes. */ var vendorPrefixes = { animationend: makePrefixMap('Animation', 'AnimationEnd'), animationiteration: makePrefixMap('Animation', 'AnimationIteration'), animationstart: makePrefixMap('Animation', 'AnimationStart'), transitionend: makePrefixMap('Transition', 'TransitionEnd') }; /** * Event names that have already been detected and prefixed (if applicable). */ var prefixedEventNames = {}; /** * Element to check for prefixes on. */ var style = {}; /** * Bootstrap if a DOM exists. */ if (ExecutionEnvironment.canUseDOM) { style = document.createElement('div').style; // On some platforms, in particular some releases of Android 4.x, // the un-prefixed "animation" and "transition" properties are defined on the // style object but the events that fire will still be prefixed, so we need // to check if the un-prefixed events are usable, and if not remove them from the map. if (!('AnimationEvent' in window)) { delete vendorPrefixes.animationend.animation; delete vendorPrefixes.animationiteration.animation; delete vendorPrefixes.animationstart.animation; } // Same as above if (!('TransitionEvent' in window)) { delete vendorPrefixes.transitionend.transition; } } /** * Attempts to determine the correct vendor prefixed event name. * * @param {string} eventName * @returns {string} */ function getVendorPrefixedEventName(eventName) { if (prefixedEventNames[eventName]) { return prefixedEventNames[eventName]; } else if (!vendorPrefixes[eventName]) { return eventName; } var prefixMap = vendorPrefixes[eventName]; for (var styleProp in prefixMap) { if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) { return prefixedEventNames[eventName] = prefixMap[styleProp]; } } return ''; } /** * Types of raw signals from the browser caught at the top level. * * For events like 'submit' which don't consistently bubble (which we * trap at a lower node than `document`), binding at `document` would * cause duplicate events so we don't include them here. */ var topLevelTypes$1 = { topAbort: 'abort', topAnimationEnd: getVendorPrefixedEventName('animationend') || 'animationend', topAnimationIteration: getVendorPrefixedEventName('animationiteration') || 'animationiteration', topAnimationStart: getVendorPrefixedEventName('animationstart') || 'animationstart', topBlur: 'blur', topCancel: 'cancel', topCanPlay: 'canplay', topCanPlayThrough: 'canplaythrough', topChange: 'change', topClick: 'click', topClose: 'close', topCompositionEnd: 'compositionend', topCompositionStart: 'compositionstart', topCompositionUpdate: 'compositionupdate', topContextMenu: 'contextmenu', topCopy: 'copy', topCut: 'cut', topDoubleClick: 'dblclick', topDrag: 'drag', topDragEnd: 'dragend', topDragEnter: 'dragenter', topDragExit: 'dragexit', topDragLeave: 'dragleave', topDragOver: 'dragover', topDragStart: 'dragstart', topDrop: 'drop', topDurationChange: 'durationchange', topEmptied: 'emptied', topEncrypted: 'encrypted', topEnded: 'ended', topError: 'error', topFocus: 'focus', topInput: 'input', topKeyDown: 'keydown', topKeyPress: 'keypress', topKeyUp: 'keyup', topLoadedData: 'loadeddata', topLoad: 'load', topLoadedMetadata: 'loadedmetadata', topLoadStart: 'loadstart', topMouseDown: 'mousedown', topMouseMove: 'mousemove', topMouseOut: 'mouseout', topMouseOver: 'mouseover', topMouseUp: 'mouseup', topPaste: 'paste', topPause: 'pause', topPlay: 'play', topPlaying: 'playing', topProgress: 'progress', topRateChange: 'ratechange', topScroll: 'scroll', topSeeked: 'seeked', topSeeking: 'seeking', topSelectionChange: 'selectionchange', topStalled: 'stalled', topSuspend: 'suspend', topTextInput: 'textInput', topTimeUpdate: 'timeupdate', topToggle: 'toggle', topTouchCancel: 'touchcancel', topTouchEnd: 'touchend', topTouchMove: 'touchmove', topTouchStart: 'touchstart', topTransitionEnd: getVendorPrefixedEventName('transitionend') || 'transitionend', topVolumeChange: 'volumechange', topWaiting: 'waiting', topWheel: 'wheel' }; var BrowserEventConstants = { topLevelTypes: topLevelTypes$1 }; function runEventQueueInBatch(events) { enqueueEvents(events); processEventQueue(false); } /** * Streams a fired top-level event to `EventPluginHub` where plugins have the * opportunity to create `ReactEvent`s to be dispatched. */ function handleTopLevel(topLevelType, targetInst, nativeEvent, nativeEventTarget) { var events = extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget); runEventQueueInBatch(events); } var topLevelTypes = BrowserEventConstants.topLevelTypes; /** * Summary of `ReactBrowserEventEmitter` event handling: * * - Top-level delegation is used to trap most native browser events. This * may only occur in the main thread and is the responsibility of * ReactDOMEventListener, which is injected and can therefore support * pluggable event sources. This is the only work that occurs in the main * thread. * * - We normalize and de-duplicate events to account for browser quirks. This * may be done in the worker thread. * * - Forward these native events (with the associated top-level type used to * trap it) to `EventPluginHub`, which in turn will ask plugins if they want * to extract any synthetic events. * * - The `EventPluginHub` will then process each event by annotating them with * "dispatches", a sequence of listeners and IDs that care about that event. * * - The `EventPluginHub` then dispatches the events. * * Overview of React and the event system: * * +------------+ . * | DOM | . * +------------+ . * | . * v . * +------------+ . * | ReactEvent | . * | Listener | . * +------------+ . +-----------+ * | . +--------+|SimpleEvent| * | . | |Plugin | * +-----|------+ . v +-----------+ * | | | . +--------------+ +------------+ * | +-----------.--->|EventPluginHub| | Event | * | | . | | +-----------+ | Propagators| * | ReactEvent | . | | |TapEvent | |------------| * | Emitter | . | |<---+|Plugin | |other plugin| * | | . | | +-----------+ | utilities | * | +-----------.--->| | +------------+ * | | | . +--------------+ * +-----|------+ . ^ +-----------+ * | . | |Enter/Leave| * + . +-------+|Plugin | * +-------------+ . +-----------+ * | application | . * |-------------| . * | | . * | | . * +-------------+ . * . * React Core . General Purpose Event Plugin System */ var alreadyListeningTo = {}; var reactTopListenersCounter = 0; /** * To ensure no conflicts with other potential React instances on the page */ var topListenersIDKey = '_reactListenersID' + ('' + Math.random()).slice(2); function getListeningForDocument(mountAt) { // In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty` // directly. if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) { mountAt[topListenersIDKey] = reactTopListenersCounter++; alreadyListeningTo[mountAt[topListenersIDKey]] = {}; } return alreadyListeningTo[mountAt[topListenersIDKey]]; } /** * We listen for bubbled touch events on the document object. * * Firefox v8.01 (and possibly others) exhibited strange behavior when * mounting `onmousemove` events at some node that was not the document * element. The symptoms were that if your mouse is not moving over something * contained within that mount point (for example on the background) the * top-level listeners for `onmousemove` won't be called. However, if you * register the `mousemove` on the document object, then it will of course * catch all `mousemove`s. This along with iOS quirks, justifies restricting * top-level listeners to the document object only, at least for these * movement types of events and possibly all events. * * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html * * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but * they bubble to document. * * @param {string} registrationName Name of listener (e.g. `onClick`). * @param {object} contentDocumentHandle Document which owns the container */ function listenTo(registrationName, contentDocumentHandle) { var mountAt = contentDocumentHandle; var isListening = getListeningForDocument(mountAt); var dependencies = registrationNameDependencies[registrationName]; for (var i = 0; i < dependencies.length; i++) { var dependency = dependencies[i]; if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) { if (dependency === 'topScroll') { trapCapturedEvent('topScroll', 'scroll', mountAt); } else if (dependency === 'topFocus' || dependency === 'topBlur') { trapCapturedEvent('topFocus', 'focus', mountAt); trapCapturedEvent('topBlur', 'blur', mountAt); // to make sure blur and focus event listeners are only attached once isListening.topBlur = true; isListening.topFocus = true; } else if (dependency === 'topCancel') { if (isEventSupported('cancel', true)) { trapCapturedEvent('topCancel', 'cancel', mountAt); } isListening.topCancel = true; } else if (dependency === 'topClose') { if (isEventSupported('close', true)) { trapCapturedEvent('topClose', 'close', mountAt); } isListening.topClose = true; } else if (topLevelTypes.hasOwnProperty(dependency)) { trapBubbledEvent(dependency, topLevelTypes[dependency], mountAt); } isListening[dependency] = true; } } } function isListeningToAllDependencies(registrationName, mountAt) { var isListening = getListeningForDocument(mountAt); var dependencies = registrationNameDependencies[registrationName]; for (var i = 0; i < dependencies.length; i++) { var dependency = dependencies[i]; if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) { return false; } } return true; } /** * Given any node return the first leaf node without children. * * @param {DOMElement|DOMTextNode} node * @return {DOMElement|DOMTextNode} */ function getLeafNode(node) { while (node && node.firstChild) { node = node.firstChild; } return node; } /** * Get the next sibling within a container. This will walk up the * DOM if a node's siblings have been exhausted. * * @param {DOMElement|DOMTextNode} node * @return {?DOMElement|DOMTextNode} */ function getSiblingNode(node) { while (node) { if (node.nextSibling) { return node.nextSibling; } node = node.parentNode; } } /** * Get object describing the nodes which contain characters at offset. * * @param {DOMElement|DOMTextNode} root * @param {number} offset * @return {?object} */ function getNodeForCharacterOffset(root, offset) { var node = getLeafNode(root); var nodeStart = 0; var nodeEnd = 0; while (node) { if (node.nodeType === TEXT_NODE) { nodeEnd = nodeStart + node.textContent.length; if (nodeStart <= offset && nodeEnd >= offset) { return { node: node, offset: offset - nodeStart }; } nodeStart = nodeEnd; } node = getLeafNode(getSiblingNode(node)); } } /** * @param {DOMElement} outerNode * @return {?object} */ function getOffsets(outerNode) { var selection = window.getSelection && window.getSelection(); if (!selection || selection.rangeCount === 0) { return null; } var anchorNode = selection.anchorNode, anchorOffset = selection.anchorOffset, focusNode$$1 = selection.focusNode, focusOffset = selection.focusOffset; // In Firefox, anchorNode and focusNode can be "anonymous divs", e.g. the // up/down buttons on an . Anonymous divs do not seem to // expose properties, triggering a "Permission denied error" if any of its // properties are accessed. The only seemingly possible way to avoid erroring // is to access a property that typically works for non-anonymous divs and // catch any error that may otherwise arise. See // https://bugzilla.mozilla.org/show_bug.cgi?id=208427 try { /* eslint-disable no-unused-expressions */ anchorNode.nodeType; focusNode$$1.nodeType; /* eslint-enable no-unused-expressions */ } catch (e) { return null; } return getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode$$1, focusOffset); } /** * Returns {start, end} where `start` is the character/codepoint index of * (anchorNode, anchorOffset) within the textContent of `outerNode`, and * `end` is the index of (focusNode, focusOffset). * * Returns null if you pass in garbage input but we should probably just crash. * * Exported only for testing. */ function getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode$$1, focusOffset) { var length = 0; var start = -1; var end = -1; var indexWithinAnchor = 0; var indexWithinFocus = 0; var node = outerNode; var parentNode = null; outer: while (true) { var next = null; while (true) { if (node === anchorNode && (anchorOffset === 0 || node.nodeType === TEXT_NODE)) { start = length + anchorOffset; } if (node === focusNode$$1 && (focusOffset === 0 || node.nodeType === TEXT_NODE)) { end = length + focusOffset; } if (node.nodeType === TEXT_NODE) { length += node.nodeValue.length; } if ((next = node.firstChild) === null) { break; } // Moving from `node` to its first child `next`. parentNode = node; node = next; } while (true) { if (node === outerNode) { // If `outerNode` has children, this is always the second time visiting // it. If it has no children, this is still the first loop, and the only // valid selection is anchorNode and focusNode both equal to this node // and both offsets 0, in which case we will have handled above. break outer; } if (parentNode === anchorNode && ++indexWithinAnchor === anchorOffset) { start = length; } if (parentNode === focusNode$$1 && ++indexWithinFocus === focusOffset) { end = length; } if ((next = node.nextSibling) !== null) { break; } node = parentNode; parentNode = node.parentNode; } // Moving from `node` to its next sibling `next`. node = next; } if (start === -1 || end === -1) { // This should never happen. (Would happen if the anchor/focus nodes aren't // actually inside the passed-in node.) return null; } return { start: start, end: end }; } /** * In modern non-IE browsers, we can support both forward and backward * selections. * * Note: IE10+ supports the Selection object, but it does not support * the `extend` method, which means that even in modern IE, it's not possible * to programmatically create a backward selection. Thus, for all IE * versions, we use the old IE API to create our selections. * * @param {DOMElement|DOMTextNode} node * @param {object} offsets */ function setOffsets(node, offsets) { if (!window.getSelection) { return; } var selection = window.getSelection(); var length = node[getTextContentAccessor()].length; var start = Math.min(offsets.start, length); var end = offsets.end === undefined ? start : Math.min(offsets.end, length); // IE 11 uses modern selection, but doesn't support the extend method. // Flip backward selections, so we can set with a single range. if (!selection.extend && start > end) { var temp = end; end = start; start = temp; } var startMarker = getNodeForCharacterOffset(node, start); var endMarker = getNodeForCharacterOffset(node, end); if (startMarker && endMarker) { if (selection.rangeCount === 1 && selection.anchorNode === startMarker.node && selection.anchorOffset === startMarker.offset && selection.focusNode === endMarker.node && selection.focusOffset === endMarker.offset) { return; } var range = document.createRange(); range.setStart(startMarker.node, startMarker.offset); selection.removeAllRanges(); if (start > end) { selection.addRange(range); selection.extend(endMarker.node, endMarker.offset); } else { range.setEnd(endMarker.node, endMarker.offset); selection.addRange(range); } } } function isInDocument(node) { return containsNode(document.documentElement, node); } /** * @ReactInputSelection: React input selection module. Based on Selection.js, * but modified to be suitable for react and has a couple of bug fixes (doesn't * assume buttons have range selections allowed). * Input selection module for React. */ function hasSelectionCapabilities(elem) { var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase(); return nodeName && (nodeName === 'input' && elem.type === 'text' || nodeName === 'textarea' || elem.contentEditable === 'true'); } function getSelectionInformation() { var focusedElem = getActiveElement(); return { focusedElem: focusedElem, selectionRange: hasSelectionCapabilities(focusedElem) ? getSelection$1(focusedElem) : null }; } /** * @restoreSelection: If any selection information was potentially lost, * restore it. This is useful when performing operations that could remove dom * nodes and place them back in, resulting in focus being lost. */ function restoreSelection(priorSelectionInformation) { var curFocusedElem = getActiveElement(); var priorFocusedElem = priorSelectionInformation.focusedElem; var priorSelectionRange = priorSelectionInformation.selectionRange; if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) { if (hasSelectionCapabilities(priorFocusedElem)) { setSelection(priorFocusedElem, priorSelectionRange); } // Focusing a node can change the scroll position, which is undesirable var ancestors = []; var ancestor = priorFocusedElem; while (ancestor = ancestor.parentNode) { if (ancestor.nodeType === ELEMENT_NODE) { ancestors.push({ element: ancestor, left: ancestor.scrollLeft, top: ancestor.scrollTop }); } } focusNode(priorFocusedElem); for (var i = 0; i < ancestors.length; i++) { var info = ancestors[i]; info.element.scrollLeft = info.left; info.element.scrollTop = info.top; } } } /** * @getSelection: Gets the selection bounds of a focused textarea, input or * contentEditable node. * -@input: Look up selection bounds of this input * -@return {start: selectionStart, end: selectionEnd} */ function getSelection$1(input) { var selection = void 0; if ('selectionStart' in input) { // Modern browser with input or textarea. selection = { start: input.selectionStart, end: input.selectionEnd }; } else { // Content editable or old IE textarea. selection = getOffsets(input); } return selection || { start: 0, end: 0 }; } /** * @setSelection: Sets the selection bounds of a textarea or input and focuses * the input. * -@input Set selection bounds of this input or textarea * -@offsets Object of same form that is returned from get* */ function setSelection(input, offsets) { var start = offsets.start, end = offsets.end; if (end === undefined) { end = start; } if ('selectionStart' in input) { input.selectionStart = start; input.selectionEnd = Math.min(end, input.value.length); } else { setOffsets(input, offsets); } } var skipSelectionChangeEvent = ExecutionEnvironment.canUseDOM && 'documentMode' in document && document.documentMode <= 11; var eventTypes$3 = { select: { phasedRegistrationNames: { bubbled: 'onSelect', captured: 'onSelectCapture' }, dependencies: ['topBlur', 'topContextMenu', 'topFocus', 'topKeyDown', 'topKeyUp', 'topMouseDown', 'topMouseUp', 'topSelectionChange'] } }; var activeElement$1 = null; var activeElementInst$1 = null; var lastSelection = null; var mouseDown = false; /** * Get an object which is a unique representation of the current selection. * * The return value will not be consistent across nodes or browsers, but * two identical selections on the same node will return identical objects. * * @param {DOMElement} node * @return {object} */ function getSelection(node) { if ('selectionStart' in node && hasSelectionCapabilities(node)) { return { start: node.selectionStart, end: node.selectionEnd }; } else if (window.getSelection) { var selection = window.getSelection(); return { anchorNode: selection.anchorNode, anchorOffset: selection.anchorOffset, focusNode: selection.focusNode, focusOffset: selection.focusOffset }; } } /** * Poll selection to see whether it's changed. * * @param {object} nativeEvent * @return {?SyntheticEvent} */ function constructSelectEvent(nativeEvent, nativeEventTarget) { // Ensure we have the right element, and that the user is not dragging a // selection (this matches native `select` event behavior). In HTML5, select // fires only on input and textarea thus if there's no focused element we // won't dispatch. if (mouseDown || activeElement$1 == null || activeElement$1 !== getActiveElement()) { return null; } // Only fire when selection has actually changed. var currentSelection = getSelection(activeElement$1); if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) { lastSelection = currentSelection; var syntheticEvent = SyntheticEvent$1.getPooled(eventTypes$3.select, activeElementInst$1, nativeEvent, nativeEventTarget); syntheticEvent.type = 'select'; syntheticEvent.target = activeElement$1; accumulateTwoPhaseDispatches(syntheticEvent); return syntheticEvent; } return null; } /** * This plugin creates an `onSelect` event that normalizes select events * across form elements. * * Supported elements are: * - input (see `isTextInputElement`) * - textarea * - contentEditable * * This differs from native browser implementations in the following ways: * - Fires on contentEditable fields as well as inputs. * - Fires for collapsed selection. * - Fires after user input. */ var SelectEventPlugin = { eventTypes: eventTypes$3, extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) { var doc = nativeEventTarget.window === nativeEventTarget ? nativeEventTarget.document : nativeEventTarget.nodeType === DOCUMENT_NODE ? nativeEventTarget : nativeEventTarget.ownerDocument; // Track whether all listeners exists for this plugin. If none exist, we do // not extract events. See #3639. if (!doc || !isListeningToAllDependencies('onSelect', doc)) { return null; } var targetNode = targetInst ? getNodeFromInstance$1(targetInst) : window; switch (topLevelType) { // Track the input node that has focus. case 'topFocus': if (isTextInputElement(targetNode) || targetNode.contentEditable === 'true') { activeElement$1 = targetNode; activeElementInst$1 = targetInst; lastSelection = null; } break; case 'topBlur': activeElement$1 = null; activeElementInst$1 = null; lastSelection = null; break; // Don't fire the event while the user is dragging. This matches the // semantics of the native select event. case 'topMouseDown': mouseDown = true; break; case 'topContextMenu': case 'topMouseUp': mouseDown = false; return constructSelectEvent(nativeEvent, nativeEventTarget); // Chrome and IE fire non-standard event when selection is changed (and // sometimes when it hasn't). IE's event fires out of order with respect // to key and input events on deletion, so we discard it. // // Firefox doesn't support selectionchange, so check selection status // after each key entry. The selection changes after keydown and before // keyup, but we check on keydown as well in the case of holding down a // key, when multiple keydown events are fired but only one keyup is. // This is also our approach for IE handling, for the reason above. case 'topSelectionChange': if (skipSelectionChangeEvent) { break; } // falls through case 'topKeyDown': case 'topKeyUp': return constructSelectEvent(nativeEvent, nativeEventTarget); } return null; } }; /** * @interface Event * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent */ var AnimationEventInterface = { animationName: null, elapsedTime: null, pseudoElement: null }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticEvent} */ function SyntheticAnimationEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticEvent$1.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticEvent$1.augmentClass(SyntheticAnimationEvent, AnimationEventInterface); /** * @interface Event * @see http://www.w3.org/TR/clipboard-apis/ */ var ClipboardEventInterface = { clipboardData: function (event) { return 'clipboardData' in event ? event.clipboardData : window.clipboardData; } }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticEvent} */ function SyntheticClipboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticEvent$1.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticEvent$1.augmentClass(SyntheticClipboardEvent, ClipboardEventInterface); /** * @interface FocusEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ var FocusEventInterface = { relatedTarget: null }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticUIEvent} */ function SyntheticFocusEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticUIEvent.augmentClass(SyntheticFocusEvent, FocusEventInterface); /** * `charCode` represents the actual "character code" and is safe to use with * `String.fromCharCode`. As such, only keys that correspond to printable * characters produce a valid `charCode`, the only exception to this is Enter. * The Tab-key is considered non-printable and does not have a `charCode`, * presumably because it does not produce a tab-character in browsers. * * @param {object} nativeEvent Native browser event. * @return {number} Normalized `charCode` property. */ function getEventCharCode(nativeEvent) { var charCode; var keyCode = nativeEvent.keyCode; if ('charCode' in nativeEvent) { charCode = nativeEvent.charCode; // FF does not set `charCode` for the Enter-key, check against `keyCode`. if (charCode === 0 && keyCode === 13) { charCode = 13; } } else { // IE8 does not implement `charCode`, but `keyCode` has the correct value. charCode = keyCode; } // Some non-printable keys are reported in `charCode`/`keyCode`, discard them. // Must not discard the (non-)printable Enter-key. if (charCode >= 32 || charCode === 13) { return charCode; } return 0; } /** * Normalization of deprecated HTML5 `key` values * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names */ var normalizeKey = { Esc: 'Escape', Spacebar: ' ', Left: 'ArrowLeft', Up: 'ArrowUp', Right: 'ArrowRight', Down: 'ArrowDown', Del: 'Delete', Win: 'OS', Menu: 'ContextMenu', Apps: 'ContextMenu', Scroll: 'ScrollLock', MozPrintableKey: 'Unidentified' }; /** * Translation from legacy `keyCode` to HTML5 `key` * Only special keys supported, all others depend on keyboard layout or browser * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names */ var translateToKey = { '8': 'Backspace', '9': 'Tab', '12': 'Clear', '13': 'Enter', '16': 'Shift', '17': 'Control', '18': 'Alt', '19': 'Pause', '20': 'CapsLock', '27': 'Escape', '32': ' ', '33': 'PageUp', '34': 'PageDown', '35': 'End', '36': 'Home', '37': 'ArrowLeft', '38': 'ArrowUp', '39': 'ArrowRight', '40': 'ArrowDown', '45': 'Insert', '46': 'Delete', '112': 'F1', '113': 'F2', '114': 'F3', '115': 'F4', '116': 'F5', '117': 'F6', '118': 'F7', '119': 'F8', '120': 'F9', '121': 'F10', '122': 'F11', '123': 'F12', '144': 'NumLock', '145': 'ScrollLock', '224': 'Meta' }; /** * @param {object} nativeEvent Native browser event. * @return {string} Normalized `key` property. */ function getEventKey(nativeEvent) { if (nativeEvent.key) { // Normalize inconsistent values reported by browsers due to // implementations of a working draft specification. // FireFox implements `key` but returns `MozPrintableKey` for all // printable characters (normalized to `Unidentified`), ignore it. var key = normalizeKey[nativeEvent.key] || nativeEvent.key; if (key !== 'Unidentified') { return key; } } // Browser does not implement `key`, polyfill as much of it as we can. if (nativeEvent.type === 'keypress') { var charCode = getEventCharCode(nativeEvent); // The enter-key is technically both printable and non-printable and can // thus be captured by `keypress`, no other non-printable key should. return charCode === 13 ? 'Enter' : String.fromCharCode(charCode); } if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') { // While user keyboard layout determines the actual meaning of each // `keyCode` value, almost all function keys have a universal value. return translateToKey[nativeEvent.keyCode] || 'Unidentified'; } return ''; } /** * @interface KeyboardEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ var KeyboardEventInterface = { key: getEventKey, location: null, ctrlKey: null, shiftKey: null, altKey: null, metaKey: null, repeat: null, locale: null, getModifierState: getEventModifierState, // Legacy Interface charCode: function (event) { // `charCode` is the result of a KeyPress event and represents the value of // the actual printable character. // KeyPress is deprecated, but its replacement is not yet final and not // implemented in any major browser. Only KeyPress has charCode. if (event.type === 'keypress') { return getEventCharCode(event); } return 0; }, keyCode: function (event) { // `keyCode` is the result of a KeyDown/Up event and represents the value of // physical keyboard key. // The actual meaning of the value depends on the users' keyboard layout // which cannot be detected. Assuming that it is a US keyboard layout // provides a surprisingly accurate mapping for US and European users. // Due to this, it is left to the user to implement at this time. if (event.type === 'keydown' || event.type === 'keyup') { return event.keyCode; } return 0; }, which: function (event) { // `which` is an alias for either `keyCode` or `charCode` depending on the // type of the event. if (event.type === 'keypress') { return getEventCharCode(event); } if (event.type === 'keydown' || event.type === 'keyup') { return event.keyCode; } return 0; } }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticUIEvent} */ function SyntheticKeyboardEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticUIEvent.augmentClass(SyntheticKeyboardEvent, KeyboardEventInterface); /** * @interface DragEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ var DragEventInterface = { dataTransfer: null }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticMouseEvent} */ function SyntheticDragEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticMouseEvent.augmentClass(SyntheticDragEvent, DragEventInterface); /** * @interface TouchEvent * @see http://www.w3.org/TR/touch-events/ */ var TouchEventInterface = { touches: null, targetTouches: null, changedTouches: null, altKey: null, metaKey: null, ctrlKey: null, shiftKey: null, getModifierState: getEventModifierState }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticUIEvent} */ function SyntheticTouchEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticUIEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticUIEvent.augmentClass(SyntheticTouchEvent, TouchEventInterface); /** * @interface Event * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events- * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent */ var TransitionEventInterface = { propertyName: null, elapsedTime: null, pseudoElement: null }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticEvent} */ function SyntheticTransitionEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticEvent$1.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticEvent$1.augmentClass(SyntheticTransitionEvent, TransitionEventInterface); /** * @interface WheelEvent * @see http://www.w3.org/TR/DOM-Level-3-Events/ */ var WheelEventInterface = { deltaX: function (event) { return 'deltaX' in event ? event.deltaX : // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive). 'wheelDeltaX' in event ? -event.wheelDeltaX : 0; }, deltaY: function (event) { return 'deltaY' in event ? event.deltaY : // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive). 'wheelDeltaY' in event ? -event.wheelDeltaY : // Fallback to `wheelDelta` for IE<9 and normalize (down is positive). 'wheelDelta' in event ? -event.wheelDelta : 0; }, deltaZ: null, // Browsers without "deltaMode" is reporting in raw wheel delta where one // notch on the scroll is always +/- 120, roughly equivalent to pixels. // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size. deltaMode: null }; /** * @param {object} dispatchConfig Configuration used to dispatch this event. * @param {string} dispatchMarker Marker identifying the event target. * @param {object} nativeEvent Native browser event. * @extends {SyntheticMouseEvent} */ function SyntheticWheelEvent(dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget) { return SyntheticMouseEvent.call(this, dispatchConfig, dispatchMarker, nativeEvent, nativeEventTarget); } SyntheticMouseEvent.augmentClass(SyntheticWheelEvent, WheelEventInterface); /** * Turns * ['abort', ...] * into * eventTypes = { * 'abort': { * phasedRegistrationNames: { * bubbled: 'onAbort', * captured: 'onAbortCapture', * }, * dependencies: ['topAbort'], * }, * ... * }; * topLevelEventsToDispatchConfig = { * 'topAbort': { sameConfig } * }; */ var eventTypes$4 = {}; var topLevelEventsToDispatchConfig = {}; ['abort', 'animationEnd', 'animationIteration', 'animationStart', 'blur', 'cancel', 'canPlay', 'canPlayThrough', 'click', 'close', 'contextMenu', 'copy', 'cut', 'doubleClick', 'drag', 'dragEnd', 'dragEnter', 'dragExit', 'dragLeave', 'dragOver', 'dragStart', 'drop', 'durationChange', 'emptied', 'encrypted', 'ended', 'error', 'focus', 'input', 'invalid', 'keyDown', 'keyPress', 'keyUp', 'load', 'loadedData', 'loadedMetadata', 'loadStart', 'mouseDown', 'mouseMove', 'mouseOut', 'mouseOver', 'mouseUp', 'paste', 'pause', 'play', 'playing', 'progress', 'rateChange', 'reset', 'scroll', 'seeked', 'seeking', 'stalled', 'submit', 'suspend', 'timeUpdate', 'toggle', 'touchCancel', 'touchEnd', 'touchMove', 'touchStart', 'transitionEnd', 'volumeChange', 'waiting', 'wheel'].forEach(function (event) { var capitalizedEvent = event[0].toUpperCase() + event.slice(1); var onEvent = 'on' + capitalizedEvent; var topEvent = 'top' + capitalizedEvent; var type = { phasedRegistrationNames: { bubbled: onEvent, captured: onEvent + 'Capture' }, dependencies: [topEvent] }; eventTypes$4[event] = type; topLevelEventsToDispatchConfig[topEvent] = type; }); // Only used in DEV for exhaustiveness validation. var knownHTMLTopLevelTypes = ['topAbort', 'topCancel', 'topCanPlay', 'topCanPlayThrough', 'topClose', 'topDurationChange', 'topEmptied', 'topEncrypted', 'topEnded', 'topError', 'topInput', 'topInvalid', 'topLoad', 'topLoadedData', 'topLoadedMetadata', 'topLoadStart', 'topPause', 'topPlay', 'topPlaying', 'topProgress', 'topRateChange', 'topReset', 'topSeeked', 'topSeeking', 'topStalled', 'topSubmit', 'topSuspend', 'topTimeUpdate', 'topToggle', 'topVolumeChange', 'topWaiting']; var SimpleEventPlugin = { eventTypes: eventTypes$4, extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) { var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType]; if (!dispatchConfig) { return null; } var EventConstructor; switch (topLevelType) { case 'topKeyPress': // Firefox creates a keypress event for function keys too. This removes // the unwanted keypress events. Enter is however both printable and // non-printable. One would expect Tab to be as well (but it isn't). if (getEventCharCode(nativeEvent) === 0) { return null; } /* falls through */ case 'topKeyDown': case 'topKeyUp': EventConstructor = SyntheticKeyboardEvent; break; case 'topBlur': case 'topFocus': EventConstructor = SyntheticFocusEvent; break; case 'topClick': // Firefox creates a click event on right mouse clicks. This removes the // unwanted click events. if (nativeEvent.button === 2) { return null; } /* falls through */ case 'topDoubleClick': case 'topMouseDown': case 'topMouseMove': case 'topMouseUp': // TODO: Disabled elements should not respond to mouse events /* falls through */ case 'topMouseOut': case 'topMouseOver': case 'topContextMenu': EventConstructor = SyntheticMouseEvent; break; case 'topDrag': case 'topDragEnd': case 'topDragEnter': case 'topDragExit': case 'topDragLeave': case 'topDragOver': case 'topDragStart': case 'topDrop': EventConstructor = SyntheticDragEvent; break; case 'topTouchCancel': case 'topTouchEnd': case 'topTouchMove': case 'topTouchStart': EventConstructor = SyntheticTouchEvent; break; case 'topAnimationEnd': case 'topAnimationIteration': case 'topAnimationStart': EventConstructor = SyntheticAnimationEvent; break; case 'topTransitionEnd': EventConstructor = SyntheticTransitionEvent; break; case 'topScroll': EventConstructor = SyntheticUIEvent; break; case 'topWheel': EventConstructor = SyntheticWheelEvent; break; case 'topCopy': case 'topCut': case 'topPaste': EventConstructor = SyntheticClipboardEvent; break; default: { if (knownHTMLTopLevelTypes.indexOf(topLevelType) === -1) { warning(false, 'SimpleEventPlugin: Unhandled event type, `%s`. This warning ' + 'is likely caused by a bug in React. Please file an issue.', topLevelType); } } // HTML Events // @see http://www.w3.org/TR/html5/index.html#events-0 EventConstructor = SyntheticEvent$1; break; } var event = EventConstructor.getPooled(dispatchConfig, targetInst, nativeEvent, nativeEventTarget); accumulateTwoPhaseDispatches(event); return event; } }; setHandleTopLevel(handleTopLevel); /** * Inject modules for resolving DOM hierarchy and plugin ordering. */ injection$1.injectEventPluginOrder(DOMEventPluginOrder); injection$2.injectComponentTree(ReactDOMComponentTree); /** * Some important event plugins included by default (without having to require * them). */ injection$1.injectEventPluginsByName({ SimpleEventPlugin: SimpleEventPlugin, EnterLeaveEventPlugin: EnterLeaveEventPlugin, ChangeEventPlugin: ChangeEventPlugin, SelectEventPlugin: SelectEventPlugin, BeforeInputEventPlugin: BeforeInputEventPlugin }); var enableAsyncSubtreeAPI = true; var enableAsyncSchedulingByDefaultInReactDOM = false; // Exports ReactDOM.createRoot var enableCreateRoot = false; var enableUserTimingAPI = true; // Mutating mode (React DOM, React ART, React Native): var enableMutatingReconciler = true; // Experimental noop mode (currently unused): var enableNoopReconciler = false; // Experimental persistent mode (CS): var enablePersistentReconciler = false; // Helps identify side effects in begin-phase lifecycle hooks and setState reducers: var debugRenderPhaseSideEffects = false; // Only used in www builds. var valueStack = []; { var fiberStack = []; } var index = -1; function createCursor(defaultValue) { return { current: defaultValue }; } function pop(cursor, fiber) { if (index < 0) { { warning(false, 'Unexpected pop.'); } return; } { if (fiber !== fiberStack[index]) { warning(false, 'Unexpected Fiber popped.'); } } cursor.current = valueStack[index]; valueStack[index] = null; { fiberStack[index] = null; } index--; } function push(cursor, value, fiber) { index++; valueStack[index] = cursor.current; { fiberStack[index] = fiber; } cursor.current = value; } function reset$1() { while (index > -1) { valueStack[index] = null; { fiberStack[index] = null; } index--; } } var describeComponentFrame = function (name, source, ownerName) { return '\n in ' + (name || 'Unknown') + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : ''); }; function describeFiber(fiber) { switch (fiber.tag) { case IndeterminateComponent: case FunctionalComponent: case ClassComponent: case HostComponent: var owner = fiber._debugOwner; var source = fiber._debugSource; var name = getComponentName(fiber); var ownerName = null; if (owner) { ownerName = getComponentName(owner); } return describeComponentFrame(name, source, ownerName); default: return ''; } } // This function can only be called with a work-in-progress fiber and // only during begin or complete phase. Do not call it under any other // circumstances. function getStackAddendumByWorkInProgressFiber(workInProgress) { var info = ''; var node = workInProgress; do { info += describeFiber(node); // Otherwise this return pointer might point to the wrong tree: node = node['return']; } while (node); return info; } function getCurrentFiberOwnerName() { { var fiber = ReactDebugCurrentFiber.current; if (fiber === null) { return null; } var owner = fiber._debugOwner; if (owner !== null && typeof owner !== 'undefined') { return getComponentName(owner); } } return null; } function getCurrentFiberStackAddendum() { { var fiber = ReactDebugCurrentFiber.current; if (fiber === null) { return null; } // Safe because if current fiber exists, we are reconciling, // and it is guaranteed to be the work-in-progress version. return getStackAddendumByWorkInProgressFiber(fiber); } return null; } function resetCurrentFiber() { ReactDebugCurrentFrame.getCurrentStack = null; ReactDebugCurrentFiber.current = null; ReactDebugCurrentFiber.phase = null; } function setCurrentFiber(fiber) { ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackAddendum; ReactDebugCurrentFiber.current = fiber; ReactDebugCurrentFiber.phase = null; } function setCurrentPhase(phase) { ReactDebugCurrentFiber.phase = phase; } var ReactDebugCurrentFiber = { current: null, phase: null, resetCurrentFiber: resetCurrentFiber, setCurrentFiber: setCurrentFiber, setCurrentPhase: setCurrentPhase, getCurrentFiberOwnerName: getCurrentFiberOwnerName, getCurrentFiberStackAddendum: getCurrentFiberStackAddendum }; // Prefix measurements so that it's possible to filter them. // Longer prefixes are hard to read in DevTools. var reactEmoji = '\u269B'; var warningEmoji = '\u26D4'; var supportsUserTiming = typeof performance !== 'undefined' && typeof performance.mark === 'function' && typeof performance.clearMarks === 'function' && typeof performance.measure === 'function' && typeof performance.clearMeasures === 'function'; // Keep track of current fiber so that we know the path to unwind on pause. // TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them? var currentFiber = null; // If we're in the middle of user code, which fiber and method is it? // Reusing `currentFiber` would be confusing for this because user code fiber // can change during commit phase too, but we don't need to unwind it (since // lifecycles in the commit phase don't resemble a tree). var currentPhase = null; var currentPhaseFiber = null; // Did lifecycle hook schedule an update? This is often a performance problem, // so we will keep track of it, and include it in the report. // Track commits caused by cascading updates. var isCommitting = false; var hasScheduledUpdateInCurrentCommit = false; var hasScheduledUpdateInCurrentPhase = false; var commitCountInCurrentWorkLoop = 0; var effectCountInCurrentCommit = 0; var isWaitingForCallback = false; // During commits, we only show a measurement once per method name // to avoid stretch the commit phase with measurement overhead. var labelsInCurrentCommit = new Set(); var formatMarkName = function (markName) { return reactEmoji + ' ' + markName; }; var formatLabel = function (label, warning$$1) { var prefix = warning$$1 ? warningEmoji + ' ' : reactEmoji + ' '; var suffix = warning$$1 ? ' Warning: ' + warning$$1 : ''; return '' + prefix + label + suffix; }; var beginMark = function (markName) { performance.mark(formatMarkName(markName)); }; var clearMark = function (markName) { performance.clearMarks(formatMarkName(markName)); }; var endMark = function (label, markName, warning$$1) { var formattedMarkName = formatMarkName(markName); var formattedLabel = formatLabel(label, warning$$1); try { performance.measure(formattedLabel, formattedMarkName); } catch (err) {} // If previous mark was missing for some reason, this will throw. // This could only happen if React crashed in an unexpected place earlier. // Don't pile on with more errors. // Clear marks immediately to avoid growing buffer. performance.clearMarks(formattedMarkName); performance.clearMeasures(formattedLabel); }; var getFiberMarkName = function (label, debugID) { return label + ' (#' + debugID + ')'; }; var getFiberLabel = function (componentName, isMounted, phase) { if (phase === null) { // These are composite component total time measurements. return componentName + ' [' + (isMounted ? 'update' : 'mount') + ']'; } else { // Composite component methods. return componentName + '.' + phase; } }; var beginFiberMark = function (fiber, phase) { var componentName = getComponentName(fiber) || 'Unknown'; var debugID = fiber._debugID; var isMounted = fiber.alternate !== null; var label = getFiberLabel(componentName, isMounted, phase); if (isCommitting && labelsInCurrentCommit.has(label)) { // During the commit phase, we don't show duplicate labels because // there is a fixed overhead for every measurement, and we don't // want to stretch the commit phase beyond necessary. return false; } labelsInCurrentCommit.add(label); var markName = getFiberMarkName(label, debugID); beginMark(markName); return true; }; var clearFiberMark = function (fiber, phase) { var componentName = getComponentName(fiber) || 'Unknown'; var debugID = fiber._debugID; var isMounted = fiber.alternate !== null; var label = getFiberLabel(componentName, isMounted, phase); var markName = getFiberMarkName(label, debugID); clearMark(markName); }; var endFiberMark = function (fiber, phase, warning$$1) { var componentName = getComponentName(fiber) || 'Unknown'; var debugID = fiber._debugID; var isMounted = fiber.alternate !== null; var label = getFiberLabel(componentName, isMounted, phase); var markName = getFiberMarkName(label, debugID); endMark(label, markName, warning$$1); }; var shouldIgnoreFiber = function (fiber) { // Host components should be skipped in the timeline. // We could check typeof fiber.type, but does this work with RN? switch (fiber.tag) { case HostRoot: case HostComponent: case HostText: case HostPortal: case ReturnComponent: case Fragment: return true; default: return false; } }; var clearPendingPhaseMeasurement = function () { if (currentPhase !== null && currentPhaseFiber !== null) { clearFiberMark(currentPhaseFiber, currentPhase); } currentPhaseFiber = null; currentPhase = null; hasScheduledUpdateInCurrentPhase = false; }; var pauseTimers = function () { // Stops all currently active measurements so that they can be resumed // if we continue in a later deferred loop from the same unit of work. var fiber = currentFiber; while (fiber) { if (fiber._debugIsCurrentlyTiming) { endFiberMark(fiber, null, null); } fiber = fiber['return']; } }; var resumeTimersRecursively = function (fiber) { if (fiber['return'] !== null) { resumeTimersRecursively(fiber['return']); } if (fiber._debugIsCurrentlyTiming) { beginFiberMark(fiber, null); } }; var resumeTimers = function () { // Resumes all measurements that were active during the last deferred loop. if (currentFiber !== null) { resumeTimersRecursively(currentFiber); } }; function recordEffect() { if (enableUserTimingAPI) { effectCountInCurrentCommit++; } } function recordScheduleUpdate() { if (enableUserTimingAPI) { if (isCommitting) { hasScheduledUpdateInCurrentCommit = true; } if (currentPhase !== null && currentPhase !== 'componentWillMount' && currentPhase !== 'componentWillReceiveProps') { hasScheduledUpdateInCurrentPhase = true; } } } function startRequestCallbackTimer() { if (enableUserTimingAPI) { if (supportsUserTiming && !isWaitingForCallback) { isWaitingForCallback = true; beginMark('(Waiting for async callback...)'); } } } function stopRequestCallbackTimer(didExpire) { if (enableUserTimingAPI) { if (supportsUserTiming) { isWaitingForCallback = false; var warning$$1 = didExpire ? 'React was blocked by main thread' : null; endMark('(Waiting for async callback...)', '(Waiting for async callback...)', warning$$1); } } } function startWorkTimer(fiber) { if (enableUserTimingAPI) { if (!supportsUserTiming || shouldIgnoreFiber(fiber)) { return; } // If we pause, this is the fiber to unwind from. currentFiber = fiber; if (!beginFiberMark(fiber, null)) { return; } fiber._debugIsCurrentlyTiming = true; } } function cancelWorkTimer(fiber) { if (enableUserTimingAPI) { if (!supportsUserTiming || shouldIgnoreFiber(fiber)) { return; } // Remember we shouldn't complete measurement for this fiber. // Otherwise flamechart will be deep even for small updates. fiber._debugIsCurrentlyTiming = false; clearFiberMark(fiber, null); } } function stopWorkTimer(fiber) { if (enableUserTimingAPI) { if (!supportsUserTiming || shouldIgnoreFiber(fiber)) { return; } // If we pause, its parent is the fiber to unwind from. currentFiber = fiber['return']; if (!fiber._debugIsCurrentlyTiming) { return; } fiber._debugIsCurrentlyTiming = false; endFiberMark(fiber, null, null); } } function stopFailedWorkTimer(fiber) { if (enableUserTimingAPI) { if (!supportsUserTiming || shouldIgnoreFiber(fiber)) { return; } // If we pause, its parent is the fiber to unwind from. currentFiber = fiber['return']; if (!fiber._debugIsCurrentlyTiming) { return; } fiber._debugIsCurrentlyTiming = false; var warning$$1 = 'An error was thrown inside this error boundary'; endFiberMark(fiber, null, warning$$1); } } function startPhaseTimer(fiber, phase) { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } clearPendingPhaseMeasurement(); if (!beginFiberMark(fiber, phase)) { return; } currentPhaseFiber = fiber; currentPhase = phase; } } function stopPhaseTimer() { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } if (currentPhase !== null && currentPhaseFiber !== null) { var warning$$1 = hasScheduledUpdateInCurrentPhase ? 'Scheduled a cascading update' : null; endFiberMark(currentPhaseFiber, currentPhase, warning$$1); } currentPhase = null; currentPhaseFiber = null; } } function startWorkLoopTimer(nextUnitOfWork) { if (enableUserTimingAPI) { currentFiber = nextUnitOfWork; if (!supportsUserTiming) { return; } commitCountInCurrentWorkLoop = 0; // This is top level call. // Any other measurements are performed within. beginMark('(React Tree Reconciliation)'); // Resume any measurements that were in progress during the last loop. resumeTimers(); } } function stopWorkLoopTimer(interruptedBy) { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } var warning$$1 = null; if (interruptedBy !== null) { if (interruptedBy.tag === HostRoot) { warning$$1 = 'A top-level update interrupted the previous render'; } else { var componentName = getComponentName(interruptedBy) || 'Unknown'; warning$$1 = 'An update to ' + componentName + ' interrupted the previous render'; } } else if (commitCountInCurrentWorkLoop > 1) { warning$$1 = 'There were cascading updates'; } commitCountInCurrentWorkLoop = 0; // Pause any measurements until the next loop. pauseTimers(); endMark('(React Tree Reconciliation)', '(React Tree Reconciliation)', warning$$1); } } function startCommitTimer() { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } isCommitting = true; hasScheduledUpdateInCurrentCommit = false; labelsInCurrentCommit.clear(); beginMark('(Committing Changes)'); } } function stopCommitTimer() { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } var warning$$1 = null; if (hasScheduledUpdateInCurrentCommit) { warning$$1 = 'Lifecycle hook scheduled a cascading update'; } else if (commitCountInCurrentWorkLoop > 0) { warning$$1 = 'Caused by a cascading update in earlier commit'; } hasScheduledUpdateInCurrentCommit = false; commitCountInCurrentWorkLoop++; isCommitting = false; labelsInCurrentCommit.clear(); endMark('(Committing Changes)', '(Committing Changes)', warning$$1); } } function startCommitHostEffectsTimer() { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } effectCountInCurrentCommit = 0; beginMark('(Committing Host Effects)'); } } function stopCommitHostEffectsTimer() { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } var count = effectCountInCurrentCommit; effectCountInCurrentCommit = 0; endMark('(Committing Host Effects: ' + count + ' Total)', '(Committing Host Effects)', null); } } function startCommitLifeCyclesTimer() { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } effectCountInCurrentCommit = 0; beginMark('(Calling Lifecycle Methods)'); } } function stopCommitLifeCyclesTimer() { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } var count = effectCountInCurrentCommit; effectCountInCurrentCommit = 0; endMark('(Calling Lifecycle Methods: ' + count + ' Total)', '(Calling Lifecycle Methods)', null); } } { var warnedAboutMissingGetChildContext = {}; } // A cursor to the current merged context object on the stack. var contextStackCursor = createCursor(emptyObject); // A cursor to a boolean indicating whether the context has changed. var didPerformWorkStackCursor = createCursor(false); // Keep track of the previous context object that was on the stack. // We use this to get access to the parent context after we have already // pushed the next context provider, and now need to merge their contexts. var previousContext = emptyObject; function getUnmaskedContext(workInProgress) { var hasOwnContext = isContextProvider(workInProgress); if (hasOwnContext) { // If the fiber is a context provider itself, when we read its context // we have already pushed its own child context on the stack. A context // provider should not "see" its own child context. Therefore we read the // previous (parent) context instead for a context provider. return previousContext; } return contextStackCursor.current; } function cacheContext(workInProgress, unmaskedContext, maskedContext) { var instance = workInProgress.stateNode; instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext; instance.__reactInternalMemoizedMaskedChildContext = maskedContext; } function getMaskedContext(workInProgress, unmaskedContext) { var type = workInProgress.type; var contextTypes = type.contextTypes; if (!contextTypes) { return emptyObject; } // Avoid recreating masked context unless unmasked context has changed. // Failing to do this will result in unnecessary calls to componentWillReceiveProps. // This may trigger infinite loops if componentWillReceiveProps calls setState. var instance = workInProgress.stateNode; if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) { return instance.__reactInternalMemoizedMaskedChildContext; } var context = {}; for (var key in contextTypes) { context[key] = unmaskedContext[key]; } { var name = getComponentName(workInProgress) || 'Unknown'; checkPropTypes(contextTypes, context, 'context', name, ReactDebugCurrentFiber.getCurrentFiberStackAddendum); } // Cache unmasked context so we can avoid recreating masked context unless necessary. // Context is created before the class component is instantiated so check for instance. if (instance) { cacheContext(workInProgress, unmaskedContext, context); } return context; } function hasContextChanged() { return didPerformWorkStackCursor.current; } function isContextConsumer(fiber) { return fiber.tag === ClassComponent && fiber.type.contextTypes != null; } function isContextProvider(fiber) { return fiber.tag === ClassComponent && fiber.type.childContextTypes != null; } function popContextProvider(fiber) { if (!isContextProvider(fiber)) { return; } pop(didPerformWorkStackCursor, fiber); pop(contextStackCursor, fiber); } function popTopLevelContextObject(fiber) { pop(didPerformWorkStackCursor, fiber); pop(contextStackCursor, fiber); } function pushTopLevelContextObject(fiber, context, didChange) { !(contextStackCursor.cursor == null) ? invariant(false, 'Unexpected context found on stack. This error is likely caused by a bug in React. Please file an issue.') : void 0; push(contextStackCursor, context, fiber); push(didPerformWorkStackCursor, didChange, fiber); } function processChildContext(fiber, parentContext) { var instance = fiber.stateNode; var childContextTypes = fiber.type.childContextTypes; // TODO (bvaughn) Replace this behavior with an invariant() in the future. // It has only been added in Fiber to match the (unintentional) behavior in Stack. if (typeof instance.getChildContext !== 'function') { { var componentName = getComponentName(fiber) || 'Unknown'; if (!warnedAboutMissingGetChildContext[componentName]) { warnedAboutMissingGetChildContext[componentName] = true; warning(false, '%s.childContextTypes is specified but there is no getChildContext() method ' + 'on the instance. You can either define getChildContext() on %s or remove ' + 'childContextTypes from it.', componentName, componentName); } } return parentContext; } var childContext = void 0; { ReactDebugCurrentFiber.setCurrentPhase('getChildContext'); } startPhaseTimer(fiber, 'getChildContext'); childContext = instance.getChildContext(); stopPhaseTimer(); { ReactDebugCurrentFiber.setCurrentPhase(null); } for (var contextKey in childContext) { !(contextKey in childContextTypes) ? invariant(false, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', getComponentName(fiber) || 'Unknown', contextKey) : void 0; } { var name = getComponentName(fiber) || 'Unknown'; checkPropTypes(childContextTypes, childContext, 'child context', name, // In practice, there is one case in which we won't get a stack. It's when // somebody calls unstable_renderSubtreeIntoContainer() and we process // context from the parent component instance. The stack will be missing // because it's outside of the reconciliation, and so the pointer has not // been set. This is rare and doesn't matter. We'll also remove that API. ReactDebugCurrentFiber.getCurrentFiberStackAddendum); } return _assign({}, parentContext, childContext); } function pushContextProvider(workInProgress) { if (!isContextProvider(workInProgress)) { return false; } var instance = workInProgress.stateNode; // We push the context as early as possible to ensure stack integrity. // If the instance does not exist yet, we will push null at first, // and replace it on the stack later when invalidating the context. var memoizedMergedChildContext = instance && instance.__reactInternalMemoizedMergedChildContext || emptyObject; // Remember the parent context so we can merge with it later. // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates. previousContext = contextStackCursor.current; push(contextStackCursor, memoizedMergedChildContext, workInProgress); push(didPerformWorkStackCursor, didPerformWorkStackCursor.current, workInProgress); return true; } function invalidateContextProvider(workInProgress, didChange) { var instance = workInProgress.stateNode; !instance ? invariant(false, 'Expected to have an instance by this point. This error is likely caused by a bug in React. Please file an issue.') : void 0; if (didChange) { // Merge parent and own context. // Skip this if we're not updating due to sCU. // This avoids unnecessarily recomputing memoized values. var mergedContext = processChildContext(workInProgress, previousContext); instance.__reactInternalMemoizedMergedChildContext = mergedContext; // Replace the old (or empty) context with the new one. // It is important to unwind the context in the reverse order. pop(didPerformWorkStackCursor, workInProgress); pop(contextStackCursor, workInProgress); // Now push the new context and mark that it has changed. push(contextStackCursor, mergedContext, workInProgress); push(didPerformWorkStackCursor, didChange, workInProgress); } else { pop(didPerformWorkStackCursor, workInProgress); push(didPerformWorkStackCursor, didChange, workInProgress); } } function resetContext() { previousContext = emptyObject; contextStackCursor.current = emptyObject; didPerformWorkStackCursor.current = false; } function findCurrentUnmaskedContext(fiber) { // Currently this is only used with renderSubtreeIntoContainer; not sure if it // makes sense elsewhere !(isFiberMounted(fiber) && fiber.tag === ClassComponent) ? invariant(false, 'Expected subtree parent to be a mounted class component. This error is likely caused by a bug in React. Please file an issue.') : void 0; var node = fiber; while (node.tag !== HostRoot) { if (isContextProvider(node)) { return node.stateNode.__reactInternalMemoizedMergedChildContext; } var parent = node['return']; !parent ? invariant(false, 'Found unexpected detached subtree parent. This error is likely caused by a bug in React. Please file an issue.') : void 0; node = parent; } return node.stateNode.context; } var NoWork = 0; // TODO: Use an opaque type once ESLint et al support the syntax var Sync = 1; var Never = 2147483647; // Max int32: Math.pow(2, 31) - 1 var UNIT_SIZE = 10; var MAGIC_NUMBER_OFFSET = 2; // 1 unit of expiration time represents 10ms. function msToExpirationTime(ms) { // Always add an offset so that we don't clash with the magic number for NoWork. return (ms / UNIT_SIZE | 0) + MAGIC_NUMBER_OFFSET; } function expirationTimeToMs(expirationTime) { return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE; } function ceiling(num, precision) { return ((num / precision | 0) + 1) * precision; } function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) { return ceiling(currentTime + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE); } var NoContext = 0; var AsyncUpdates = 1; { var hasBadMapPolyfill = false; try { var nonExtensibleObject = Object.preventExtensions({}); /* eslint-disable no-new */ /* eslint-enable no-new */ } catch (e) { // TODO: Consider warning about bad polyfills hasBadMapPolyfill = true; } } // A Fiber is work on a Component that needs to be done or was done. There can // be more than one per component. { var debugCounter = 1; } function FiberNode(tag, key, internalContextTag) { // Instance this.tag = tag; this.key = key; this.type = null; this.stateNode = null; // Fiber this['return'] = null; this.child = null; this.sibling = null; this.index = 0; this.ref = null; this.pendingProps = null; this.memoizedProps = null; this.updateQueue = null; this.memoizedState = null; this.internalContextTag = internalContextTag; // Effects this.effectTag = NoEffect; this.nextEffect = null; this.firstEffect = null; this.lastEffect = null; this.expirationTime = NoWork; this.alternate = null; { this._debugID = debugCounter++; this._debugSource = null; this._debugOwner = null; this._debugIsCurrentlyTiming = false; if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') { Object.preventExtensions(this); } } } // This is a constructor function, rather than a POJO constructor, still // please ensure we do the following: // 1) Nobody should add any instance methods on this. Instance methods can be // more difficult to predict when they get optimized and they are almost // never inlined properly in static compilers. // 2) Nobody should rely on `instanceof Fiber` for type testing. We should // always know when it is a fiber. // 3) We might want to experiment with using numeric keys since they are easier // to optimize in a non-JIT environment. // 4) We can easily go from a constructor to a createFiber object literal if that // is faster. // 5) It should be easy to port this to a C struct and keep a C implementation // compatible. var createFiber = function (tag, key, internalContextTag) { // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors return new FiberNode(tag, key, internalContextTag); }; function shouldConstruct(Component) { return !!(Component.prototype && Component.prototype.isReactComponent); } // This is used to create an alternate fiber to do work on. function createWorkInProgress(current, pendingProps, expirationTime) { var workInProgress = current.alternate; if (workInProgress === null) { // We use a double buffering pooling technique because we know that we'll // only ever need at most two versions of a tree. We pool the "other" unused // node that we're free to reuse. This is lazily created to avoid allocating // extra objects for things that are never updated. It also allow us to // reclaim the extra memory if needed. workInProgress = createFiber(current.tag, current.key, current.internalContextTag); workInProgress.type = current.type; workInProgress.stateNode = current.stateNode; { // DEV-only fields workInProgress._debugID = current._debugID; workInProgress._debugSource = current._debugSource; workInProgress._debugOwner = current._debugOwner; } workInProgress.alternate = current; current.alternate = workInProgress; } else { // We already have an alternate. // Reset the effect tag. workInProgress.effectTag = NoEffect; // The effect list is no longer valid. workInProgress.nextEffect = null; workInProgress.firstEffect = null; workInProgress.lastEffect = null; } workInProgress.expirationTime = expirationTime; workInProgress.pendingProps = pendingProps; workInProgress.child = current.child; workInProgress.memoizedProps = current.memoizedProps; workInProgress.memoizedState = current.memoizedState; workInProgress.updateQueue = current.updateQueue; // These will be overridden during the parent's reconciliation workInProgress.sibling = current.sibling; workInProgress.index = current.index; workInProgress.ref = current.ref; return workInProgress; } function createHostRootFiber() { var fiber = createFiber(HostRoot, null, NoContext); return fiber; } function createFiberFromElement(element, internalContextTag, expirationTime) { var owner = null; { owner = element._owner; } var fiber = void 0; var type = element.type, key = element.key; if (typeof type === 'function') { fiber = shouldConstruct(type) ? createFiber(ClassComponent, key, internalContextTag) : createFiber(IndeterminateComponent, key, internalContextTag); fiber.type = type; fiber.pendingProps = element.props; } else if (typeof type === 'string') { fiber = createFiber(HostComponent, key, internalContextTag); fiber.type = type; fiber.pendingProps = element.props; } else if (typeof type === 'object' && type !== null && typeof type.tag === 'number') { // Currently assumed to be a continuation and therefore is a fiber already. // TODO: The yield system is currently broken for updates in some cases. // The reified yield stores a fiber, but we don't know which fiber that is; // the current or a workInProgress? When the continuation gets rendered here // we don't know if we can reuse that fiber or if we need to clone it. // There is probably a clever way to restructure this. fiber = type; fiber.pendingProps = element.props; } else { var info = ''; { if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) { info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports."; } var ownerName = owner ? getComponentName(owner) : null; if (ownerName) { info += '\n\nCheck the render method of `' + ownerName + '`.'; } } invariant(false, 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s', type == null ? type : typeof type, info); } { fiber._debugSource = element._source; fiber._debugOwner = element._owner; } fiber.expirationTime = expirationTime; return fiber; } function createFiberFromFragment(elements, internalContextTag, expirationTime, key) { var fiber = createFiber(Fragment, key, internalContextTag); fiber.pendingProps = elements; fiber.expirationTime = expirationTime; return fiber; } function createFiberFromText(content, internalContextTag, expirationTime) { var fiber = createFiber(HostText, null, internalContextTag); fiber.pendingProps = content; fiber.expirationTime = expirationTime; return fiber; } function createFiberFromHostInstanceForDeletion() { var fiber = createFiber(HostComponent, null, NoContext); fiber.type = 'DELETED'; return fiber; } function createFiberFromCall(call, internalContextTag, expirationTime) { var fiber = createFiber(CallComponent, call.key, internalContextTag); fiber.type = call.handler; fiber.pendingProps = call; fiber.expirationTime = expirationTime; return fiber; } function createFiberFromReturn(returnNode, internalContextTag, expirationTime) { var fiber = createFiber(ReturnComponent, null, internalContextTag); fiber.expirationTime = expirationTime; return fiber; } function createFiberFromPortal(portal, internalContextTag, expirationTime) { var fiber = createFiber(HostPortal, portal.key, internalContextTag); fiber.pendingProps = portal.children || []; fiber.expirationTime = expirationTime; fiber.stateNode = { containerInfo: portal.containerInfo, pendingChildren: null, // Used by persistent updates implementation: portal.implementation }; return fiber; } function createFiberRoot(containerInfo, hydrate) { // Cyclic construction. This cheats the type system right now because // stateNode is any. var uninitializedFiber = createHostRootFiber(); var root = { current: uninitializedFiber, containerInfo: containerInfo, pendingChildren: null, remainingExpirationTime: NoWork, isReadyForCommit: false, finishedWork: null, context: null, pendingContext: null, hydrate: hydrate, nextScheduledRoot: null }; uninitializedFiber.stateNode = root; return root; } var onCommitFiberRoot = null; var onCommitFiberUnmount = null; var hasLoggedError = false; function catchErrors(fn) { return function (arg) { try { return fn(arg); } catch (err) { if (true && !hasLoggedError) { hasLoggedError = true; warning(false, 'React DevTools encountered an error: %s', err); } } }; } function injectInternals(internals) { if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { // No DevTools return false; } var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__; if (hook.isDisabled) { // This isn't a real property on the hook, but it can be set to opt out // of DevTools integration and associated warnings and logs. // https://github.com/facebook/react/issues/3877 return true; } if (!hook.supportsFiber) { { warning(false, 'The installed version of React DevTools is too old and will not work ' + 'with the current version of React. Please update React DevTools. ' + 'https://fb.me/react-devtools'); } // DevTools exists, even though it doesn't support Fiber. return true; } try { var rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks. onCommitFiberRoot = catchErrors(function (root) { return hook.onCommitFiberRoot(rendererID, root); }); onCommitFiberUnmount = catchErrors(function (fiber) { return hook.onCommitFiberUnmount(rendererID, fiber); }); } catch (err) { // Catch all errors because it is unsafe to throw during initialization. { warning(false, 'React DevTools encountered an error: %s.', err); } } // DevTools exists return true; } function onCommitRoot(root) { if (typeof onCommitFiberRoot === 'function') { onCommitFiberRoot(root); } } function onCommitUnmount(fiber) { if (typeof onCommitFiberUnmount === 'function') { onCommitFiberUnmount(fiber); } } { var didWarnUpdateInsideUpdate = false; } // Callbacks are not validated until invocation // Singly linked-list of updates. When an update is scheduled, it is added to // the queue of the current fiber and the work-in-progress fiber. The two queues // are separate but they share a persistent structure. // // During reconciliation, updates are removed from the work-in-progress fiber, // but they remain on the current fiber. That ensures that if a work-in-progress // is aborted, the aborted updates are recovered by cloning from current. // // The work-in-progress queue is always a subset of the current queue. // // When the tree is committed, the work-in-progress becomes the current. function createUpdateQueue(baseState) { var queue = { baseState: baseState, expirationTime: NoWork, first: null, last: null, callbackList: null, hasForceUpdate: false, isInitialized: false }; { queue.isProcessing = false; } return queue; } function insertUpdateIntoQueue(queue, update) { // Append the update to the end of the list. if (queue.last === null) { // Queue is empty queue.first = queue.last = update; } else { queue.last.next = update; queue.last = update; } if (queue.expirationTime === NoWork || queue.expirationTime > update.expirationTime) { queue.expirationTime = update.expirationTime; } } function insertUpdateIntoFiber(fiber, update) { // We'll have at least one and at most two distinct update queues. var alternateFiber = fiber.alternate; var queue1 = fiber.updateQueue; if (queue1 === null) { // TODO: We don't know what the base state will be until we begin work. // It depends on which fiber is the next current. Initialize with an empty // base state, then set to the memoizedState when rendering. Not super // happy with this approach. queue1 = fiber.updateQueue = createUpdateQueue(null); } var queue2 = void 0; if (alternateFiber !== null) { queue2 = alternateFiber.updateQueue; if (queue2 === null) { queue2 = alternateFiber.updateQueue = createUpdateQueue(null); } } else { queue2 = null; } queue2 = queue2 !== queue1 ? queue2 : null; // Warn if an update is scheduled from inside an updater function. { if ((queue1.isProcessing || queue2 !== null && queue2.isProcessing) && !didWarnUpdateInsideUpdate) { warning(false, 'An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.'); didWarnUpdateInsideUpdate = true; } } // If there's only one queue, add the update to that queue and exit. if (queue2 === null) { insertUpdateIntoQueue(queue1, update); return; } // If either queue is empty, we need to add to both queues. if (queue1.last === null || queue2.last === null) { insertUpdateIntoQueue(queue1, update); insertUpdateIntoQueue(queue2, update); return; } // If both lists are not empty, the last update is the same for both lists // because of structural sharing. So, we should only append to one of // the lists. insertUpdateIntoQueue(queue1, update); // But we still need to update the `last` pointer of queue2. queue2.last = update; } function getUpdateExpirationTime(fiber) { if (fiber.tag !== ClassComponent && fiber.tag !== HostRoot) { return NoWork; } var updateQueue = fiber.updateQueue; if (updateQueue === null) { return NoWork; } return updateQueue.expirationTime; } function getStateFromUpdate(update, instance, prevState, props) { var partialState = update.partialState; if (typeof partialState === 'function') { var updateFn = partialState; // Invoke setState callback an extra time to help detect side-effects. if (debugRenderPhaseSideEffects) { updateFn.call(instance, prevState, props); } return updateFn.call(instance, prevState, props); } else { return partialState; } } function processUpdateQueue(current, workInProgress, queue, instance, props, renderExpirationTime) { if (current !== null && current.updateQueue === queue) { // We need to create a work-in-progress queue, by cloning the current queue. var currentQueue = queue; queue = workInProgress.updateQueue = { baseState: currentQueue.baseState, expirationTime: currentQueue.expirationTime, first: currentQueue.first, last: currentQueue.last, isInitialized: currentQueue.isInitialized, // These fields are no longer valid because they were already committed. // Reset them. callbackList: null, hasForceUpdate: false }; } { // Set this flag so we can warn if setState is called inside the update // function of another setState. queue.isProcessing = true; } // Reset the remaining expiration time. If we skip over any updates, we'll // increase this accordingly. queue.expirationTime = NoWork; // TODO: We don't know what the base state will be until we begin work. // It depends on which fiber is the next current. Initialize with an empty // base state, then set to the memoizedState when rendering. Not super // happy with this approach. var state = void 0; if (queue.isInitialized) { state = queue.baseState; } else { state = queue.baseState = workInProgress.memoizedState; queue.isInitialized = true; } var dontMutatePrevState = true; var update = queue.first; var didSkip = false; while (update !== null) { var updateExpirationTime = update.expirationTime; if (updateExpirationTime > renderExpirationTime) { // This update does not have sufficient priority. Skip it. var remainingExpirationTime = queue.expirationTime; if (remainingExpirationTime === NoWork || remainingExpirationTime > updateExpirationTime) { // Update the remaining expiration time. queue.expirationTime = updateExpirationTime; } if (!didSkip) { didSkip = true; queue.baseState = state; } // Continue to the next update. update = update.next; continue; } // This update does have sufficient priority. // If no previous updates were skipped, drop this update from the queue by // advancing the head of the list. if (!didSkip) { queue.first = update.next; if (queue.first === null) { queue.last = null; } } // Process the update var _partialState = void 0; if (update.isReplace) { state = getStateFromUpdate(update, instance, state, props); dontMutatePrevState = true; } else { _partialState = getStateFromUpdate(update, instance, state, props); if (_partialState) { if (dontMutatePrevState) { // $FlowFixMe: Idk how to type this properly. state = _assign({}, state, _partialState); } else { state = _assign(state, _partialState); } dontMutatePrevState = false; } } if (update.isForced) { queue.hasForceUpdate = true; } if (update.callback !== null) { // Append to list of callbacks. var _callbackList = queue.callbackList; if (_callbackList === null) { _callbackList = queue.callbackList = []; } _callbackList.push(update); } update = update.next; } if (queue.callbackList !== null) { workInProgress.effectTag |= Callback; } else if (queue.first === null && !queue.hasForceUpdate) { // The queue is empty. We can reset it. workInProgress.updateQueue = null; } if (!didSkip) { didSkip = true; queue.baseState = state; } { // No longer processing. queue.isProcessing = false; } return state; } function commitCallbacks(queue, context) { var callbackList = queue.callbackList; if (callbackList === null) { return; } // Set the list to null to make sure they don't get called more than once. queue.callbackList = null; for (var i = 0; i < callbackList.length; i++) { var update = callbackList[i]; var _callback = update.callback; // This update might be processed again. Clear the callback so it's only // called once. update.callback = null; !(typeof _callback === 'function') ? invariant(false, 'Invalid argument passed as callback. Expected a function. Instead received: %s', _callback) : void 0; _callback.call(context); } } var fakeInternalInstance = {}; var isArray = Array.isArray; { var didWarnAboutStateAssignmentForComponent = {}; var warnOnInvalidCallback = function (callback, callerName) { warning(callback === null || typeof callback === 'function', '%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback); }; // This is so gross but it's at least non-critical and can be removed if // it causes problems. This is meant to give a nicer error message for // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component, // ...)) which otherwise throws a "_processChildContext is not a function" // exception. Object.defineProperty(fakeInternalInstance, '_processChildContext', { enumerable: false, value: function () { invariant(false, '_processChildContext is not available in React 16+. This likely means you have multiple copies of React and are attempting to nest a React 15 tree inside a React 16 tree using unstable_renderSubtreeIntoContainer, which isn\'t supported. Try to make sure you have only one copy of React (and ideally, switch to ReactDOM.createPortal).'); } }); Object.freeze(fakeInternalInstance); } var ReactFiberClassComponent = function (scheduleWork, computeExpirationForFiber, memoizeProps, memoizeState) { // Class component state updater var updater = { isMounted: isMounted, enqueueSetState: function (instance, partialState, callback) { var fiber = get(instance); callback = callback === undefined ? null : callback; { warnOnInvalidCallback(callback, 'setState'); } var expirationTime = computeExpirationForFiber(fiber); var update = { expirationTime: expirationTime, partialState: partialState, callback: callback, isReplace: false, isForced: false, nextCallback: null, next: null }; insertUpdateIntoFiber(fiber, update); scheduleWork(fiber, expirationTime); }, enqueueReplaceState: function (instance, state, callback) { var fiber = get(instance); callback = callback === undefined ? null : callback; { warnOnInvalidCallback(callback, 'replaceState'); } var expirationTime = computeExpirationForFiber(fiber); var update = { expirationTime: expirationTime, partialState: state, callback: callback, isReplace: true, isForced: false, nextCallback: null, next: null }; insertUpdateIntoFiber(fiber, update); scheduleWork(fiber, expirationTime); }, enqueueForceUpdate: function (instance, callback) { var fiber = get(instance); callback = callback === undefined ? null : callback; { warnOnInvalidCallback(callback, 'forceUpdate'); } var expirationTime = computeExpirationForFiber(fiber); var update = { expirationTime: expirationTime, partialState: null, callback: callback, isReplace: false, isForced: true, nextCallback: null, next: null }; insertUpdateIntoFiber(fiber, update); scheduleWork(fiber, expirationTime); } }; function checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext) { if (oldProps === null || workInProgress.updateQueue !== null && workInProgress.updateQueue.hasForceUpdate) { // If the workInProgress already has an Update effect, return true return true; } var instance = workInProgress.stateNode; var type = workInProgress.type; if (typeof instance.shouldComponentUpdate === 'function') { startPhaseTimer(workInProgress, 'shouldComponentUpdate'); var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, newContext); stopPhaseTimer(); // Simulate an async bailout/interruption by invoking lifecycle twice. if (debugRenderPhaseSideEffects) { instance.shouldComponentUpdate(newProps, newState, newContext); } { warning(shouldUpdate !== undefined, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentName(workInProgress) || 'Unknown'); } return shouldUpdate; } if (type.prototype && type.prototype.isPureReactComponent) { return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState); } return true; } function checkClassInstance(workInProgress) { var instance = workInProgress.stateNode; var type = workInProgress.type; { var name = getComponentName(workInProgress); var renderPresent = instance.render; if (!renderPresent) { if (type.prototype && typeof type.prototype.render === 'function') { warning(false, '%s(...): No `render` method found on the returned component ' + 'instance: did you accidentally return an object from the constructor?', name); } else { warning(false, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', name); } } var noGetInitialStateOnES6 = !instance.getInitialState || instance.getInitialState.isReactClassApproved || instance.state; warning(noGetInitialStateOnES6, 'getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', name); var noGetDefaultPropsOnES6 = !instance.getDefaultProps || instance.getDefaultProps.isReactClassApproved; warning(noGetDefaultPropsOnES6, 'getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', name); var noInstancePropTypes = !instance.propTypes; warning(noInstancePropTypes, 'propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', name); var noInstanceContextTypes = !instance.contextTypes; warning(noInstanceContextTypes, 'contextTypes was defined as an instance property on %s. Use a static ' + 'property to define contextTypes instead.', name); var noComponentShouldUpdate = typeof instance.componentShouldUpdate !== 'function'; warning(noComponentShouldUpdate, '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', name); if (type.prototype && type.prototype.isPureReactComponent && typeof instance.shouldComponentUpdate !== 'undefined') { warning(false, '%s has a method called shouldComponentUpdate(). ' + 'shouldComponentUpdate should not be used when extending React.PureComponent. ' + 'Please extend React.Component if shouldComponentUpdate is used.', getComponentName(workInProgress) || 'A pure component'); } var noComponentDidUnmount = typeof instance.componentDidUnmount !== 'function'; warning(noComponentDidUnmount, '%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', name); var noComponentDidReceiveProps = typeof instance.componentDidReceiveProps !== 'function'; warning(noComponentDidReceiveProps, '%s has a method called ' + 'componentDidReceiveProps(). But there is no such lifecycle method. ' + 'If you meant to update the state in response to changing props, ' + 'use componentWillReceiveProps(). If you meant to fetch data or ' + 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().', name); var noComponentWillRecieveProps = typeof instance.componentWillRecieveProps !== 'function'; warning(noComponentWillRecieveProps, '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', name); var hasMutatedProps = instance.props !== workInProgress.pendingProps; warning(instance.props === undefined || !hasMutatedProps, '%s(...): When calling super() in `%s`, make sure to pass ' + "up the same props that your component's constructor was passed.", name, name); var noInstanceDefaultProps = !instance.defaultProps; warning(noInstanceDefaultProps, 'Setting defaultProps as an instance property on %s is not supported and will be ignored.' + ' Instead, define defaultProps as a static property on %s.', name, name); } var state = instance.state; if (state && (typeof state !== 'object' || isArray(state))) { warning(false, '%s.state: must be set to an object or null', getComponentName(workInProgress)); } if (typeof instance.getChildContext === 'function') { warning(typeof workInProgress.type.childContextTypes === 'object', '%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', getComponentName(workInProgress)); } } function resetInputPointers(workInProgress, instance) { instance.props = workInProgress.memoizedProps; instance.state = workInProgress.memoizedState; } function adoptClassInstance(workInProgress, instance) { instance.updater = updater; workInProgress.stateNode = instance; // The instance needs access to the fiber so that it can schedule updates set(instance, workInProgress); { instance._reactInternalInstance = fakeInternalInstance; } } function constructClassInstance(workInProgress, props) { var ctor = workInProgress.type; var unmaskedContext = getUnmaskedContext(workInProgress); var needsContext = isContextConsumer(workInProgress); var context = needsContext ? getMaskedContext(workInProgress, unmaskedContext) : emptyObject; var instance = new ctor(props, context); adoptClassInstance(workInProgress, instance); // Cache unmasked context so we can avoid recreating masked context unless necessary. // ReactFiberContext usually updates this cache but can't for newly-created instances. if (needsContext) { cacheContext(workInProgress, unmaskedContext, context); } return instance; } function callComponentWillMount(workInProgress, instance) { startPhaseTimer(workInProgress, 'componentWillMount'); var oldState = instance.state; instance.componentWillMount(); stopPhaseTimer(); // Simulate an async bailout/interruption by invoking lifecycle twice. if (debugRenderPhaseSideEffects) { instance.componentWillMount(); } if (oldState !== instance.state) { { warning(false, '%s.componentWillMount(): Assigning directly to this.state is ' + "deprecated (except inside a component's " + 'constructor). Use setState instead.', getComponentName(workInProgress)); } updater.enqueueReplaceState(instance, instance.state, null); } } function callComponentWillReceiveProps(workInProgress, instance, newProps, newContext) { startPhaseTimer(workInProgress, 'componentWillReceiveProps'); var oldState = instance.state; instance.componentWillReceiveProps(newProps, newContext); stopPhaseTimer(); // Simulate an async bailout/interruption by invoking lifecycle twice. if (debugRenderPhaseSideEffects) { instance.componentWillReceiveProps(newProps, newContext); } if (instance.state !== oldState) { { var componentName = getComponentName(workInProgress) || 'Component'; if (!didWarnAboutStateAssignmentForComponent[componentName]) { warning(false, '%s.componentWillReceiveProps(): Assigning directly to ' + "this.state is deprecated (except inside a component's " + 'constructor). Use setState instead.', componentName); didWarnAboutStateAssignmentForComponent[componentName] = true; } } updater.enqueueReplaceState(instance, instance.state, null); } } // Invokes the mount life-cycles on a previously never rendered instance. function mountClassInstance(workInProgress, renderExpirationTime) { var current = workInProgress.alternate; { checkClassInstance(workInProgress); } var instance = workInProgress.stateNode; var state = instance.state || null; var props = workInProgress.pendingProps; !props ? invariant(false, 'There must be pending props for an initial mount. This error is likely caused by a bug in React. Please file an issue.') : void 0; var unmaskedContext = getUnmaskedContext(workInProgress); instance.props = props; instance.state = workInProgress.memoizedState = state; instance.refs = emptyObject; instance.context = getMaskedContext(workInProgress, unmaskedContext); if (enableAsyncSubtreeAPI && workInProgress.type != null && workInProgress.type.prototype != null && workInProgress.type.prototype.unstable_isAsyncReactComponent === true) { workInProgress.internalContextTag |= AsyncUpdates; } if (typeof instance.componentWillMount === 'function') { callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's // process them now. var updateQueue = workInProgress.updateQueue; if (updateQueue !== null) { instance.state = processUpdateQueue(current, workInProgress, updateQueue, instance, props, renderExpirationTime); } } if (typeof instance.componentDidMount === 'function') { workInProgress.effectTag |= Update; } } // Called on a preexisting class instance. Returns false if a resumed render // could be reused. // function resumeMountClassInstance( // workInProgress: Fiber, // priorityLevel: PriorityLevel, // ): boolean { // const instance = workInProgress.stateNode; // resetInputPointers(workInProgress, instance); // let newState = workInProgress.memoizedState; // let newProps = workInProgress.pendingProps; // if (!newProps) { // // If there isn't any new props, then we'll reuse the memoized props. // // This could be from already completed work. // newProps = workInProgress.memoizedProps; // invariant( // newProps != null, // 'There should always be pending or memoized props. This error is ' + // 'likely caused by a bug in React. Please file an issue.', // ); // } // const newUnmaskedContext = getUnmaskedContext(workInProgress); // const newContext = getMaskedContext(workInProgress, newUnmaskedContext); // const oldContext = instance.context; // const oldProps = workInProgress.memoizedProps; // if ( // typeof instance.componentWillReceiveProps === 'function' && // (oldProps !== newProps || oldContext !== newContext) // ) { // callComponentWillReceiveProps( // workInProgress, // instance, // newProps, // newContext, // ); // } // // Process the update queue before calling shouldComponentUpdate // const updateQueue = workInProgress.updateQueue; // if (updateQueue !== null) { // newState = processUpdateQueue( // workInProgress, // updateQueue, // instance, // newState, // newProps, // priorityLevel, // ); // } // // TODO: Should we deal with a setState that happened after the last // // componentWillMount and before this componentWillMount? Probably // // unsupported anyway. // if ( // !checkShouldComponentUpdate( // workInProgress, // workInProgress.memoizedProps, // newProps, // workInProgress.memoizedState, // newState, // newContext, // ) // ) { // // Update the existing instance's state, props, and context pointers even // // though we're bailing out. // instance.props = newProps; // instance.state = newState; // instance.context = newContext; // return false; // } // // Update the input pointers now so that they are correct when we call // // componentWillMount // instance.props = newProps; // instance.state = newState; // instance.context = newContext; // if (typeof instance.componentWillMount === 'function') { // callComponentWillMount(workInProgress, instance); // // componentWillMount may have called setState. Process the update queue. // const newUpdateQueue = workInProgress.updateQueue; // if (newUpdateQueue !== null) { // newState = processUpdateQueue( // workInProgress, // newUpdateQueue, // instance, // newState, // newProps, // priorityLevel, // ); // } // } // if (typeof instance.componentDidMount === 'function') { // workInProgress.effectTag |= Update; // } // instance.state = newState; // return true; // } // Invokes the update life-cycles and returns false if it shouldn't rerender. function updateClassInstance(current, workInProgress, renderExpirationTime) { var instance = workInProgress.stateNode; resetInputPointers(workInProgress, instance); var oldProps = workInProgress.memoizedProps; var newProps = workInProgress.pendingProps; if (!newProps) { // If there aren't any new props, then we'll reuse the memoized props. // This could be from already completed work. newProps = oldProps; !(newProps != null) ? invariant(false, 'There should always be pending or memoized props. This error is likely caused by a bug in React. Please file an issue.') : void 0; } var oldContext = instance.context; var newUnmaskedContext = getUnmaskedContext(workInProgress); var newContext = getMaskedContext(workInProgress, newUnmaskedContext); // Note: During these life-cycles, instance.props/instance.state are what // ever the previously attempted to render - not the "current". However, // during componentDidUpdate we pass the "current" props. if (typeof instance.componentWillReceiveProps === 'function' && (oldProps !== newProps || oldContext !== newContext)) { callComponentWillReceiveProps(workInProgress, instance, newProps, newContext); } // Compute the next state using the memoized state and the update queue. var oldState = workInProgress.memoizedState; // TODO: Previous state can be null. var newState = void 0; if (workInProgress.updateQueue !== null) { newState = processUpdateQueue(current, workInProgress, workInProgress.updateQueue, instance, newProps, renderExpirationTime); } else { newState = oldState; } if (oldProps === newProps && oldState === newState && !hasContextChanged() && !(workInProgress.updateQueue !== null && workInProgress.updateQueue.hasForceUpdate)) { // If an update was already in progress, we should schedule an Update // effect even though we're bailing out, so that cWU/cDU are called. if (typeof instance.componentDidUpdate === 'function') { if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) { workInProgress.effectTag |= Update; } } return false; } var shouldUpdate = checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext); if (shouldUpdate) { if (typeof instance.componentWillUpdate === 'function') { startPhaseTimer(workInProgress, 'componentWillUpdate'); instance.componentWillUpdate(newProps, newState, newContext); stopPhaseTimer(); // Simulate an async bailout/interruption by invoking lifecycle twice. if (debugRenderPhaseSideEffects) { instance.componentWillUpdate(newProps, newState, newContext); } } if (typeof instance.componentDidUpdate === 'function') { workInProgress.effectTag |= Update; } } else { // If an update was already in progress, we should schedule an Update // effect even though we're bailing out, so that cWU/cDU are called. if (typeof instance.componentDidUpdate === 'function') { if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) { workInProgress.effectTag |= Update; } } // If shouldComponentUpdate returned false, we should still update the // memoized props/state to indicate that this work can be reused. memoizeProps(workInProgress, newProps); memoizeState(workInProgress, newState); } // Update the existing instance's state, props, and context pointers even // if shouldComponentUpdate returns false. instance.props = newProps; instance.state = newState; instance.context = newContext; return shouldUpdate; } return { adoptClassInstance: adoptClassInstance, constructClassInstance: constructClassInstance, mountClassInstance: mountClassInstance, // resumeMountClassInstance, updateClassInstance: updateClassInstance }; }; // The Symbol used to tag the ReactElement-like types. If there is no native Symbol // nor polyfill, then a plain number is used for performance. var hasSymbol = typeof Symbol === 'function' && Symbol['for']; var REACT_ELEMENT_TYPE = hasSymbol ? Symbol['for']('react.element') : 0xeac7; var REACT_CALL_TYPE = hasSymbol ? Symbol['for']('react.call') : 0xeac8; var REACT_RETURN_TYPE = hasSymbol ? Symbol['for']('react.return') : 0xeac9; var REACT_PORTAL_TYPE = hasSymbol ? Symbol['for']('react.portal') : 0xeaca; var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol['for']('react.fragment') : 0xeacb; var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; var FAUX_ITERATOR_SYMBOL = '@@iterator'; function getIteratorFn(maybeIterable) { if (maybeIterable === null || typeof maybeIterable === 'undefined') { return null; } var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]; if (typeof maybeIterator === 'function') { return maybeIterator; } return null; } var getCurrentFiberStackAddendum$1 = ReactDebugCurrentFiber.getCurrentFiberStackAddendum; { var didWarnAboutMaps = false; /** * Warn if there's no key explicitly set on dynamic arrays of children or * object keys are not valid. This allows us to keep track of children between * updates. */ var ownerHasKeyUseWarning = {}; var ownerHasFunctionTypeWarning = {}; var warnForMissingKey = function (child) { if (child === null || typeof child !== 'object') { return; } if (!child._store || child._store.validated || child.key != null) { return; } !(typeof child._store === 'object') ? invariant(false, 'React Component in warnForMissingKey should have a _store. This error is likely caused by a bug in React. Please file an issue.') : void 0; child._store.validated = true; var currentComponentErrorInfo = 'Each child in an array or iterator should have a unique ' + '"key" prop. See https://fb.me/react-warning-keys for ' + 'more information.' + (getCurrentFiberStackAddendum$1() || ''); if (ownerHasKeyUseWarning[currentComponentErrorInfo]) { return; } ownerHasKeyUseWarning[currentComponentErrorInfo] = true; warning(false, 'Each child in an array or iterator should have a unique ' + '"key" prop. See https://fb.me/react-warning-keys for ' + 'more information.%s', getCurrentFiberStackAddendum$1()); }; } var isArray$1 = Array.isArray; function coerceRef(current, element) { var mixedRef = element.ref; if (mixedRef !== null && typeof mixedRef !== 'function') { if (element._owner) { var owner = element._owner; var inst = void 0; if (owner) { var ownerFiber = owner; !(ownerFiber.tag === ClassComponent) ? invariant(false, 'Stateless function components cannot have refs.') : void 0; inst = ownerFiber.stateNode; } !inst ? invariant(false, 'Missing owner for string ref %s. This error is likely caused by a bug in React. Please file an issue.', mixedRef) : void 0; var stringRef = '' + mixedRef; // Check if previous string ref matches new string ref if (current !== null && current.ref !== null && current.ref._stringRef === stringRef) { return current.ref; } var ref = function (value) { var refs = inst.refs === emptyObject ? inst.refs = {} : inst.refs; if (value === null) { delete refs[stringRef]; } else { refs[stringRef] = value; } }; ref._stringRef = stringRef; return ref; } else { !(typeof mixedRef === 'string') ? invariant(false, 'Expected ref to be a function or a string.') : void 0; !element._owner ? invariant(false, 'Element ref was specified as a string (%s) but no owner was set. You may have multiple copies of React loaded. (details: https://fb.me/react-refs-must-have-owner).', mixedRef) : void 0; } } return mixedRef; } function throwOnInvalidObjectType(returnFiber, newChild) { if (returnFiber.type !== 'textarea') { var addendum = ''; { addendum = ' If you meant to render a collection of children, use an array ' + 'instead.' + (getCurrentFiberStackAddendum$1() || ''); } invariant(false, 'Objects are not valid as a React child (found: %s).%s', Object.prototype.toString.call(newChild) === '[object Object]' ? 'object with keys {' + Object.keys(newChild).join(', ') + '}' : newChild, addendum); } } function warnOnFunctionType() { var currentComponentErrorInfo = 'Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of from render. ' + 'Or maybe you meant to call this function rather than return it.' + (getCurrentFiberStackAddendum$1() || ''); if (ownerHasFunctionTypeWarning[currentComponentErrorInfo]) { return; } ownerHasFunctionTypeWarning[currentComponentErrorInfo] = true; warning(false, 'Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of from render. ' + 'Or maybe you meant to call this function rather than return it.%s', getCurrentFiberStackAddendum$1() || ''); } // This wrapper function exists because I expect to clone the code in each path // to be able to optimize each path individually by branching early. This needs // a compiler or we can do it manually. Helpers that don't need this branching // live outside of this function. function ChildReconciler(shouldTrackSideEffects) { function deleteChild(returnFiber, childToDelete) { if (!shouldTrackSideEffects) { // Noop. return; } // Deletions are added in reversed order so we add it to the front. // At this point, the return fiber's effect list is empty except for // deletions, so we can just append the deletion to the list. The remaining // effects aren't added until the complete phase. Once we implement // resuming, this may not be true. var last = returnFiber.lastEffect; if (last !== null) { last.nextEffect = childToDelete; returnFiber.lastEffect = childToDelete; } else { returnFiber.firstEffect = returnFiber.lastEffect = childToDelete; } childToDelete.nextEffect = null; childToDelete.effectTag = Deletion; } function deleteRemainingChildren(returnFiber, currentFirstChild) { if (!shouldTrackSideEffects) { // Noop. return null; } // TODO: For the shouldClone case, this could be micro-optimized a bit by // assuming that after the first child we've already added everything. var childToDelete = currentFirstChild; while (childToDelete !== null) { deleteChild(returnFiber, childToDelete); childToDelete = childToDelete.sibling; } return null; } function mapRemainingChildren(returnFiber, currentFirstChild) { // Add the remaining children to a temporary map so that we can find them by // keys quickly. Implicit (null) keys get added to this set with their index var existingChildren = new Map(); var existingChild = currentFirstChild; while (existingChild !== null) { if (existingChild.key !== null) { existingChildren.set(existingChild.key, existingChild); } else { existingChildren.set(existingChild.index, existingChild); } existingChild = existingChild.sibling; } return existingChildren; } function useFiber(fiber, pendingProps, expirationTime) { // We currently set sibling to null and index to 0 here because it is easy // to forget to do before returning it. E.g. for the single child case. var clone = createWorkInProgress(fiber, pendingProps, expirationTime); clone.index = 0; clone.sibling = null; return clone; } function placeChild(newFiber, lastPlacedIndex, newIndex) { newFiber.index = newIndex; if (!shouldTrackSideEffects) { // Noop. return lastPlacedIndex; } var current = newFiber.alternate; if (current !== null) { var oldIndex = current.index; if (oldIndex < lastPlacedIndex) { // This is a move. newFiber.effectTag = Placement; return lastPlacedIndex; } else { // This item can stay in place. return oldIndex; } } else { // This is an insertion. newFiber.effectTag = Placement; return lastPlacedIndex; } } function placeSingleChild(newFiber) { // This is simpler for the single child case. We only need to do a // placement for inserting new children. if (shouldTrackSideEffects && newFiber.alternate === null) { newFiber.effectTag = Placement; } return newFiber; } function updateTextNode(returnFiber, current, textContent, expirationTime) { if (current === null || current.tag !== HostText) { // Insert var created = createFiberFromText(textContent, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } else { // Update var existing = useFiber(current, textContent, expirationTime); existing['return'] = returnFiber; return existing; } } function updateElement(returnFiber, current, element, expirationTime) { if (current !== null && current.type === element.type) { // Move based on index var existing = useFiber(current, element.props, expirationTime); existing.ref = coerceRef(current, element); existing['return'] = returnFiber; { existing._debugSource = element._source; existing._debugOwner = element._owner; } return existing; } else { // Insert var created = createFiberFromElement(element, returnFiber.internalContextTag, expirationTime); created.ref = coerceRef(current, element); created['return'] = returnFiber; return created; } } function updateCall(returnFiber, current, call, expirationTime) { // TODO: Should this also compare handler to determine whether to reuse? if (current === null || current.tag !== CallComponent) { // Insert var created = createFiberFromCall(call, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } else { // Move based on index var existing = useFiber(current, call, expirationTime); existing['return'] = returnFiber; return existing; } } function updateReturn(returnFiber, current, returnNode, expirationTime) { if (current === null || current.tag !== ReturnComponent) { // Insert var created = createFiberFromReturn(returnNode, returnFiber.internalContextTag, expirationTime); created.type = returnNode.value; created['return'] = returnFiber; return created; } else { // Move based on index var existing = useFiber(current, null, expirationTime); existing.type = returnNode.value; existing['return'] = returnFiber; return existing; } } function updatePortal(returnFiber, current, portal, expirationTime) { if (current === null || current.tag !== HostPortal || current.stateNode.containerInfo !== portal.containerInfo || current.stateNode.implementation !== portal.implementation) { // Insert var created = createFiberFromPortal(portal, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } else { // Update var existing = useFiber(current, portal.children || [], expirationTime); existing['return'] = returnFiber; return existing; } } function updateFragment(returnFiber, current, fragment, expirationTime, key) { if (current === null || current.tag !== Fragment) { // Insert var created = createFiberFromFragment(fragment, returnFiber.internalContextTag, expirationTime, key); created['return'] = returnFiber; return created; } else { // Update var existing = useFiber(current, fragment, expirationTime); existing['return'] = returnFiber; return existing; } } function createChild(returnFiber, newChild, expirationTime) { if (typeof newChild === 'string' || typeof newChild === 'number') { // Text nodes don't have keys. If the previous node is implicitly keyed // we can continue to replace it without aborting even if it is not a text // node. var created = createFiberFromText('' + newChild, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } if (typeof newChild === 'object' && newChild !== null) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: { if (newChild.type === REACT_FRAGMENT_TYPE) { var _created = createFiberFromFragment(newChild.props.children, returnFiber.internalContextTag, expirationTime, newChild.key); _created['return'] = returnFiber; return _created; } else { var _created2 = createFiberFromElement(newChild, returnFiber.internalContextTag, expirationTime); _created2.ref = coerceRef(null, newChild); _created2['return'] = returnFiber; return _created2; } } case REACT_CALL_TYPE: { var _created3 = createFiberFromCall(newChild, returnFiber.internalContextTag, expirationTime); _created3['return'] = returnFiber; return _created3; } case REACT_RETURN_TYPE: { var _created4 = createFiberFromReturn(newChild, returnFiber.internalContextTag, expirationTime); _created4.type = newChild.value; _created4['return'] = returnFiber; return _created4; } case REACT_PORTAL_TYPE: { var _created5 = createFiberFromPortal(newChild, returnFiber.internalContextTag, expirationTime); _created5['return'] = returnFiber; return _created5; } } if (isArray$1(newChild) || getIteratorFn(newChild)) { var _created6 = createFiberFromFragment(newChild, returnFiber.internalContextTag, expirationTime, null); _created6['return'] = returnFiber; return _created6; } throwOnInvalidObjectType(returnFiber, newChild); } { if (typeof newChild === 'function') { warnOnFunctionType(); } } return null; } function updateSlot(returnFiber, oldFiber, newChild, expirationTime) { // Update the fiber if the keys match, otherwise return null. var key = oldFiber !== null ? oldFiber.key : null; if (typeof newChild === 'string' || typeof newChild === 'number') { // Text nodes don't have keys. If the previous node is implicitly keyed // we can continue to replace it without aborting even if it is not a text // node. if (key !== null) { return null; } return updateTextNode(returnFiber, oldFiber, '' + newChild, expirationTime); } if (typeof newChild === 'object' && newChild !== null) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: { if (newChild.key === key) { if (newChild.type === REACT_FRAGMENT_TYPE) { return updateFragment(returnFiber, oldFiber, newChild.props.children, expirationTime, key); } return updateElement(returnFiber, oldFiber, newChild, expirationTime); } else { return null; } } case REACT_CALL_TYPE: { if (newChild.key === key) { return updateCall(returnFiber, oldFiber, newChild, expirationTime); } else { return null; } } case REACT_RETURN_TYPE: { // Returns don't have keys. If the previous node is implicitly keyed // we can continue to replace it without aborting even if it is not a // yield. if (key === null) { return updateReturn(returnFiber, oldFiber, newChild, expirationTime); } else { return null; } } case REACT_PORTAL_TYPE: { if (newChild.key === key) { return updatePortal(returnFiber, oldFiber, newChild, expirationTime); } else { return null; } } } if (isArray$1(newChild) || getIteratorFn(newChild)) { if (key !== null) { return null; } return updateFragment(returnFiber, oldFiber, newChild, expirationTime, null); } throwOnInvalidObjectType(returnFiber, newChild); } { if (typeof newChild === 'function') { warnOnFunctionType(); } } return null; } function updateFromMap(existingChildren, returnFiber, newIdx, newChild, expirationTime) { if (typeof newChild === 'string' || typeof newChild === 'number') { // Text nodes don't have keys, so we neither have to check the old nor // new node for the key. If both are text nodes, they match. var matchedFiber = existingChildren.get(newIdx) || null; return updateTextNode(returnFiber, matchedFiber, '' + newChild, expirationTime); } if (typeof newChild === 'object' && newChild !== null) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: { var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null; if (newChild.type === REACT_FRAGMENT_TYPE) { return updateFragment(returnFiber, _matchedFiber, newChild.props.children, expirationTime, newChild.key); } return updateElement(returnFiber, _matchedFiber, newChild, expirationTime); } case REACT_CALL_TYPE: { var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null; return updateCall(returnFiber, _matchedFiber2, newChild, expirationTime); } case REACT_RETURN_TYPE: { // Returns don't have keys, so we neither have to check the old nor // new node for the key. If both are returns, they match. var _matchedFiber3 = existingChildren.get(newIdx) || null; return updateReturn(returnFiber, _matchedFiber3, newChild, expirationTime); } case REACT_PORTAL_TYPE: { var _matchedFiber4 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null; return updatePortal(returnFiber, _matchedFiber4, newChild, expirationTime); } } if (isArray$1(newChild) || getIteratorFn(newChild)) { var _matchedFiber5 = existingChildren.get(newIdx) || null; return updateFragment(returnFiber, _matchedFiber5, newChild, expirationTime, null); } throwOnInvalidObjectType(returnFiber, newChild); } { if (typeof newChild === 'function') { warnOnFunctionType(); } } return null; } /** * Warns if there is a duplicate or missing key */ function warnOnInvalidKey(child, knownKeys) { { if (typeof child !== 'object' || child === null) { return knownKeys; } switch (child.$$typeof) { case REACT_ELEMENT_TYPE: case REACT_CALL_TYPE: case REACT_PORTAL_TYPE: warnForMissingKey(child); var key = child.key; if (typeof key !== 'string') { break; } if (knownKeys === null) { knownKeys = new Set(); knownKeys.add(key); break; } if (!knownKeys.has(key)) { knownKeys.add(key); break; } warning(false, 'Encountered two children with the same key, `%s`. ' + 'Keys should be unique so that components maintain their identity ' + 'across updates. Non-unique keys may cause children to be ' + 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.%s', key, getCurrentFiberStackAddendum$1()); break; default: break; } } return knownKeys; } function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, expirationTime) { // This algorithm can't optimize by searching from boths ends since we // don't have backpointers on fibers. I'm trying to see how far we can get // with that model. If it ends up not being worth the tradeoffs, we can // add it later. // Even with a two ended optimization, we'd want to optimize for the case // where there are few changes and brute force the comparison instead of // going for the Map. It'd like to explore hitting that path first in // forward-only mode and only go for the Map once we notice that we need // lots of look ahead. This doesn't handle reversal as well as two ended // search but that's unusual. Besides, for the two ended optimization to // work on Iterables, we'd need to copy the whole set. // In this first iteration, we'll just live with hitting the bad case // (adding everything to a Map) in for every insert/move. // If you change this code, also update reconcileChildrenIterator() which // uses the same algorithm. { // First, validate keys. var knownKeys = null; for (var i = 0; i < newChildren.length; i++) { var child = newChildren[i]; knownKeys = warnOnInvalidKey(child, knownKeys); } } var resultingFirstChild = null; var previousNewFiber = null; var oldFiber = currentFirstChild; var lastPlacedIndex = 0; var newIdx = 0; var nextOldFiber = null; for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) { if (oldFiber.index > newIdx) { nextOldFiber = oldFiber; oldFiber = null; } else { nextOldFiber = oldFiber.sibling; } var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], expirationTime); if (newFiber === null) { // TODO: This breaks on empty slots like null children. That's // unfortunate because it triggers the slow path all the time. We need // a better way to communicate whether this was a miss or null, // boolean, undefined, etc. if (oldFiber === null) { oldFiber = nextOldFiber; } break; } if (shouldTrackSideEffects) { if (oldFiber && newFiber.alternate === null) { // We matched the slot, but we didn't reuse the existing fiber, so we // need to delete the existing child. deleteChild(returnFiber, oldFiber); } } lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); if (previousNewFiber === null) { // TODO: Move out of the loop. This only happens for the first run. resultingFirstChild = newFiber; } else { // TODO: Defer siblings if we're not at the right index for this slot. // I.e. if we had null values before, then we want to defer this // for each null value. However, we also don't want to call updateSlot // with the previous one. previousNewFiber.sibling = newFiber; } previousNewFiber = newFiber; oldFiber = nextOldFiber; } if (newIdx === newChildren.length) { // We've reached the end of the new children. We can delete the rest. deleteRemainingChildren(returnFiber, oldFiber); return resultingFirstChild; } if (oldFiber === null) { // If we don't have any more existing children we can choose a fast path // since the rest will all be insertions. for (; newIdx < newChildren.length; newIdx++) { var _newFiber = createChild(returnFiber, newChildren[newIdx], expirationTime); if (!_newFiber) { continue; } lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx); if (previousNewFiber === null) { // TODO: Move out of the loop. This only happens for the first run. resultingFirstChild = _newFiber; } else { previousNewFiber.sibling = _newFiber; } previousNewFiber = _newFiber; } return resultingFirstChild; } // Add all children to a key map for quick lookups. var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves. for (; newIdx < newChildren.length; newIdx++) { var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], expirationTime); if (_newFiber2) { if (shouldTrackSideEffects) { if (_newFiber2.alternate !== null) { // The new fiber is a work in progress, but if there exists a // current, that means that we reused the fiber. We need to delete // it from the child list so that we don't add it to the deletion // list. existingChildren['delete'](_newFiber2.key === null ? newIdx : _newFiber2.key); } } lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx); if (previousNewFiber === null) { resultingFirstChild = _newFiber2; } else { previousNewFiber.sibling = _newFiber2; } previousNewFiber = _newFiber2; } } if (shouldTrackSideEffects) { // Any existing children that weren't consumed above were deleted. We need // to add them to the deletion list. existingChildren.forEach(function (child) { return deleteChild(returnFiber, child); }); } return resultingFirstChild; } function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, expirationTime) { // This is the same implementation as reconcileChildrenArray(), // but using the iterator instead. var iteratorFn = getIteratorFn(newChildrenIterable); !(typeof iteratorFn === 'function') ? invariant(false, 'An object is not an iterable. This error is likely caused by a bug in React. Please file an issue.') : void 0; { // Warn about using Maps as children if (typeof newChildrenIterable.entries === 'function') { var possibleMap = newChildrenIterable; if (possibleMap.entries === iteratorFn) { warning(didWarnAboutMaps, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.%s', getCurrentFiberStackAddendum$1()); didWarnAboutMaps = true; } } // First, validate keys. // We'll get a different iterator later for the main pass. var _newChildren = iteratorFn.call(newChildrenIterable); if (_newChildren) { var knownKeys = null; var _step = _newChildren.next(); for (; !_step.done; _step = _newChildren.next()) { var child = _step.value; knownKeys = warnOnInvalidKey(child, knownKeys); } } } var newChildren = iteratorFn.call(newChildrenIterable); !(newChildren != null) ? invariant(false, 'An iterable object provided no iterator.') : void 0; var resultingFirstChild = null; var previousNewFiber = null; var oldFiber = currentFirstChild; var lastPlacedIndex = 0; var newIdx = 0; var nextOldFiber = null; var step = newChildren.next(); for (; oldFiber !== null && !step.done; newIdx++, step = newChildren.next()) { if (oldFiber.index > newIdx) { nextOldFiber = oldFiber; oldFiber = null; } else { nextOldFiber = oldFiber.sibling; } var newFiber = updateSlot(returnFiber, oldFiber, step.value, expirationTime); if (newFiber === null) { // TODO: This breaks on empty slots like null children. That's // unfortunate because it triggers the slow path all the time. We need // a better way to communicate whether this was a miss or null, // boolean, undefined, etc. if (!oldFiber) { oldFiber = nextOldFiber; } break; } if (shouldTrackSideEffects) { if (oldFiber && newFiber.alternate === null) { // We matched the slot, but we didn't reuse the existing fiber, so we // need to delete the existing child. deleteChild(returnFiber, oldFiber); } } lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); if (previousNewFiber === null) { // TODO: Move out of the loop. This only happens for the first run. resultingFirstChild = newFiber; } else { // TODO: Defer siblings if we're not at the right index for this slot. // I.e. if we had null values before, then we want to defer this // for each null value. However, we also don't want to call updateSlot // with the previous one. previousNewFiber.sibling = newFiber; } previousNewFiber = newFiber; oldFiber = nextOldFiber; } if (step.done) { // We've reached the end of the new children. We can delete the rest. deleteRemainingChildren(returnFiber, oldFiber); return resultingFirstChild; } if (oldFiber === null) { // If we don't have any more existing children we can choose a fast path // since the rest will all be insertions. for (; !step.done; newIdx++, step = newChildren.next()) { var _newFiber3 = createChild(returnFiber, step.value, expirationTime); if (_newFiber3 === null) { continue; } lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx); if (previousNewFiber === null) { // TODO: Move out of the loop. This only happens for the first run. resultingFirstChild = _newFiber3; } else { previousNewFiber.sibling = _newFiber3; } previousNewFiber = _newFiber3; } return resultingFirstChild; } // Add all children to a key map for quick lookups. var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves. for (; !step.done; newIdx++, step = newChildren.next()) { var _newFiber4 = updateFromMap(existingChildren, returnFiber, newIdx, step.value, expirationTime); if (_newFiber4 !== null) { if (shouldTrackSideEffects) { if (_newFiber4.alternate !== null) { // The new fiber is a work in progress, but if there exists a // current, that means that we reused the fiber. We need to delete // it from the child list so that we don't add it to the deletion // list. existingChildren['delete'](_newFiber4.key === null ? newIdx : _newFiber4.key); } } lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx); if (previousNewFiber === null) { resultingFirstChild = _newFiber4; } else { previousNewFiber.sibling = _newFiber4; } previousNewFiber = _newFiber4; } } if (shouldTrackSideEffects) { // Any existing children that weren't consumed above were deleted. We need // to add them to the deletion list. existingChildren.forEach(function (child) { return deleteChild(returnFiber, child); }); } return resultingFirstChild; } function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, expirationTime) { // There's no need to check for keys on text nodes since we don't have a // way to define them. if (currentFirstChild !== null && currentFirstChild.tag === HostText) { // We already have an existing node so let's just update it and delete // the rest. deleteRemainingChildren(returnFiber, currentFirstChild.sibling); var existing = useFiber(currentFirstChild, textContent, expirationTime); existing['return'] = returnFiber; return existing; } // The existing first child is not a text node so we need to create one // and delete the existing ones. deleteRemainingChildren(returnFiber, currentFirstChild); var created = createFiberFromText(textContent, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) { var key = element.key; var child = currentFirstChild; while (child !== null) { // TODO: If key === null and child.key === null, then this only applies to // the first item in the list. if (child.key === key) { if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.type === element.type) { deleteRemainingChildren(returnFiber, child.sibling); var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime); existing.ref = coerceRef(child, element); existing['return'] = returnFiber; { existing._debugSource = element._source; existing._debugOwner = element._owner; } return existing; } else { deleteRemainingChildren(returnFiber, child); break; } } else { deleteChild(returnFiber, child); } child = child.sibling; } if (element.type === REACT_FRAGMENT_TYPE) { var created = createFiberFromFragment(element.props.children, returnFiber.internalContextTag, expirationTime, element.key); created['return'] = returnFiber; return created; } else { var _created7 = createFiberFromElement(element, returnFiber.internalContextTag, expirationTime); _created7.ref = coerceRef(currentFirstChild, element); _created7['return'] = returnFiber; return _created7; } } function reconcileSingleCall(returnFiber, currentFirstChild, call, expirationTime) { var key = call.key; var child = currentFirstChild; while (child !== null) { // TODO: If key === null and child.key === null, then this only applies to // the first item in the list. if (child.key === key) { if (child.tag === CallComponent) { deleteRemainingChildren(returnFiber, child.sibling); var existing = useFiber(child, call, expirationTime); existing['return'] = returnFiber; return existing; } else { deleteRemainingChildren(returnFiber, child); break; } } else { deleteChild(returnFiber, child); } child = child.sibling; } var created = createFiberFromCall(call, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } function reconcileSingleReturn(returnFiber, currentFirstChild, returnNode, expirationTime) { // There's no need to check for keys on yields since they're stateless. var child = currentFirstChild; if (child !== null) { if (child.tag === ReturnComponent) { deleteRemainingChildren(returnFiber, child.sibling); var existing = useFiber(child, null, expirationTime); existing.type = returnNode.value; existing['return'] = returnFiber; return existing; } else { deleteRemainingChildren(returnFiber, child); } } var created = createFiberFromReturn(returnNode, returnFiber.internalContextTag, expirationTime); created.type = returnNode.value; created['return'] = returnFiber; return created; } function reconcileSinglePortal(returnFiber, currentFirstChild, portal, expirationTime) { var key = portal.key; var child = currentFirstChild; while (child !== null) { // TODO: If key === null and child.key === null, then this only applies to // the first item in the list. if (child.key === key) { if (child.tag === HostPortal && child.stateNode.containerInfo === portal.containerInfo && child.stateNode.implementation === portal.implementation) { deleteRemainingChildren(returnFiber, child.sibling); var existing = useFiber(child, portal.children || [], expirationTime); existing['return'] = returnFiber; return existing; } else { deleteRemainingChildren(returnFiber, child); break; } } else { deleteChild(returnFiber, child); } child = child.sibling; } var created = createFiberFromPortal(portal, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } // This API will tag the children with the side-effect of the reconciliation // itself. They will be added to the side-effect list as we pass through the // children and the parent. function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) { // This function is not recursive. // If the top level item is an array, we treat it as a set of children, // not as a fragment. Nested arrays on the other hand will be treated as // fragment nodes. Recursion happens at the normal flow. // Handle top level unkeyed fragments as if they were arrays. // This leads to an ambiguity between <>{[...]} and <>.... // We treat the ambiguous cases above the same. if (typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null) { newChild = newChild.props.children; } // Handle object types var isObject = typeof newChild === 'object' && newChild !== null; if (isObject) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime)); case REACT_CALL_TYPE: return placeSingleChild(reconcileSingleCall(returnFiber, currentFirstChild, newChild, expirationTime)); case REACT_RETURN_TYPE: return placeSingleChild(reconcileSingleReturn(returnFiber, currentFirstChild, newChild, expirationTime)); case REACT_PORTAL_TYPE: return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime)); } } if (typeof newChild === 'string' || typeof newChild === 'number') { return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, expirationTime)); } if (isArray$1(newChild)) { return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime); } if (getIteratorFn(newChild)) { return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, expirationTime); } if (isObject) { throwOnInvalidObjectType(returnFiber, newChild); } { if (typeof newChild === 'function') { warnOnFunctionType(); } } if (typeof newChild === 'undefined') { // If the new child is undefined, and the return fiber is a composite // component, throw an error. If Fiber return types are disabled, // we already threw above. switch (returnFiber.tag) { case ClassComponent: { { var instance = returnFiber.stateNode; if (instance.render._isMockFunction) { // We allow auto-mocks to proceed as if they're returning null. break; } } } // Intentionally fall through to the next case, which handles both // functions and classes // eslint-disable-next-lined no-fallthrough case FunctionalComponent: { var Component = returnFiber.type; invariant(false, '%s(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.', Component.displayName || Component.name || 'Component'); } } } // Remaining cases are all treated as empty. return deleteRemainingChildren(returnFiber, currentFirstChild); } return reconcileChildFibers; } var reconcileChildFibers = ChildReconciler(true); var mountChildFibers = ChildReconciler(false); function cloneChildFibers(current, workInProgress) { !(current === null || workInProgress.child === current.child) ? invariant(false, 'Resuming work not yet implemented.') : void 0; if (workInProgress.child === null) { return; } var currentChild = workInProgress.child; var newChild = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime); workInProgress.child = newChild; newChild['return'] = workInProgress; while (currentChild.sibling !== null) { currentChild = currentChild.sibling; newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime); newChild['return'] = workInProgress; } newChild.sibling = null; } { var warnedAboutStatelessRefs = {}; } var ReactFiberBeginWork = function (config, hostContext, hydrationContext, scheduleWork, computeExpirationForFiber) { var shouldSetTextContent = config.shouldSetTextContent, useSyncScheduling = config.useSyncScheduling, shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree; var pushHostContext = hostContext.pushHostContext, pushHostContainer = hostContext.pushHostContainer; var enterHydrationState = hydrationContext.enterHydrationState, resetHydrationState = hydrationContext.resetHydrationState, tryToClaimNextHydratableInstance = hydrationContext.tryToClaimNextHydratableInstance; var _ReactFiberClassCompo = ReactFiberClassComponent(scheduleWork, computeExpirationForFiber, memoizeProps, memoizeState), adoptClassInstance = _ReactFiberClassCompo.adoptClassInstance, constructClassInstance = _ReactFiberClassCompo.constructClassInstance, mountClassInstance = _ReactFiberClassCompo.mountClassInstance, updateClassInstance = _ReactFiberClassCompo.updateClassInstance; // TODO: Remove this and use reconcileChildrenAtExpirationTime directly. function reconcileChildren(current, workInProgress, nextChildren) { reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, workInProgress.expirationTime); } function reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, renderExpirationTime) { if (current === null) { // If this is a fresh new component that hasn't been rendered yet, we // won't update its child set by applying minimal side-effects. Instead, // we will add them all to the child before it gets rendered. That means // we can optimize this reconciliation pass by not tracking side-effects. workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime); } else { // If the current child is the same as the work in progress, it means that // we haven't yet started any work on these children. Therefore, we use // the clone algorithm to create a copy of all the current children. // If we had any progressed work already, that is invalid at this point so // let's throw it out. workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderExpirationTime); } } function updateFragment(current, workInProgress) { var nextChildren = workInProgress.pendingProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. if (nextChildren === null) { nextChildren = workInProgress.memoizedProps; } } else if (nextChildren === null || workInProgress.memoizedProps === nextChildren) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } reconcileChildren(current, workInProgress, nextChildren); memoizeProps(workInProgress, nextChildren); return workInProgress.child; } function markRef(current, workInProgress) { var ref = workInProgress.ref; if (ref !== null && (!current || current.ref !== ref)) { // Schedule a Ref effect workInProgress.effectTag |= Ref; } } function updateFunctionalComponent(current, workInProgress) { var fn = workInProgress.type; var nextProps = workInProgress.pendingProps; var memoizedProps = workInProgress.memoizedProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. if (nextProps === null) { nextProps = memoizedProps; } } else { if (nextProps === null || memoizedProps === nextProps) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } // TODO: consider bringing fn.shouldComponentUpdate() back. // It used to be here. } var unmaskedContext = getUnmaskedContext(workInProgress); var context = getMaskedContext(workInProgress, unmaskedContext); var nextChildren; { ReactCurrentOwner.current = workInProgress; ReactDebugCurrentFiber.setCurrentPhase('render'); nextChildren = fn(nextProps, context); ReactDebugCurrentFiber.setCurrentPhase(null); } // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; reconcileChildren(current, workInProgress, nextChildren); memoizeProps(workInProgress, nextProps); return workInProgress.child; } function updateClassComponent(current, workInProgress, renderExpirationTime) { // Push context providers early to prevent context stack mismatches. // During mounting we don't know the child context yet as the instance doesn't exist. // We will invalidate the child context in finishClassComponent() right after rendering. var hasContext = pushContextProvider(workInProgress); var shouldUpdate = void 0; if (current === null) { if (!workInProgress.stateNode) { // In the initial pass we might need to construct the instance. constructClassInstance(workInProgress, workInProgress.pendingProps); mountClassInstance(workInProgress, renderExpirationTime); shouldUpdate = true; } else { invariant(false, 'Resuming work not yet implemented.'); // In a resume, we'll already have an instance we can reuse. // shouldUpdate = resumeMountClassInstance(workInProgress, renderExpirationTime); } } else { shouldUpdate = updateClassInstance(current, workInProgress, renderExpirationTime); } return finishClassComponent(current, workInProgress, shouldUpdate, hasContext); } function finishClassComponent(current, workInProgress, shouldUpdate, hasContext) { // Refs should update even if shouldComponentUpdate returns false markRef(current, workInProgress); if (!shouldUpdate) { // Context providers should defer to sCU for rendering if (hasContext) { invalidateContextProvider(workInProgress, false); } return bailoutOnAlreadyFinishedWork(current, workInProgress); } var instance = workInProgress.stateNode; // Rerender ReactCurrentOwner.current = workInProgress; var nextChildren = void 0; { ReactDebugCurrentFiber.setCurrentPhase('render'); nextChildren = instance.render(); if (debugRenderPhaseSideEffects) { instance.render(); } ReactDebugCurrentFiber.setCurrentPhase(null); } // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; reconcileChildren(current, workInProgress, nextChildren); // Memoize props and state using the values we just used to render. // TODO: Restructure so we never read values from the instance. memoizeState(workInProgress, instance.state); memoizeProps(workInProgress, instance.props); // The context might have changed so we need to recalculate it. if (hasContext) { invalidateContextProvider(workInProgress, true); } return workInProgress.child; } function pushHostRootContext(workInProgress) { var root = workInProgress.stateNode; if (root.pendingContext) { pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext !== root.context); } else if (root.context) { // Should always be set pushTopLevelContextObject(workInProgress, root.context, false); } pushHostContainer(workInProgress, root.containerInfo); } function updateHostRoot(current, workInProgress, renderExpirationTime) { pushHostRootContext(workInProgress); var updateQueue = workInProgress.updateQueue; if (updateQueue !== null) { var prevState = workInProgress.memoizedState; var state = processUpdateQueue(current, workInProgress, updateQueue, null, null, renderExpirationTime); if (prevState === state) { // If the state is the same as before, that's a bailout because we had // no work that expires at this time. resetHydrationState(); return bailoutOnAlreadyFinishedWork(current, workInProgress); } var element = state.element; var root = workInProgress.stateNode; if ((current === null || current.child === null) && root.hydrate && enterHydrationState(workInProgress)) { // If we don't have any current children this might be the first pass. // We always try to hydrate. If this isn't a hydration pass there won't // be any children to hydrate which is effectively the same thing as // not hydrating. // This is a bit of a hack. We track the host root as a placement to // know that we're currently in a mounting state. That way isMounted // works as expected. We must reset this before committing. // TODO: Delete this when we delete isMounted and findDOMNode. workInProgress.effectTag |= Placement; // Ensure that children mount into this root without tracking // side-effects. This ensures that we don't store Placement effects on // nodes that will be hydrated. workInProgress.child = mountChildFibers(workInProgress, null, element, renderExpirationTime); } else { // Otherwise reset hydration state in case we aborted and resumed another // root. resetHydrationState(); reconcileChildren(current, workInProgress, element); } memoizeState(workInProgress, state); return workInProgress.child; } resetHydrationState(); // If there is no update queue, that's a bailout because the root has no props. return bailoutOnAlreadyFinishedWork(current, workInProgress); } function updateHostComponent(current, workInProgress, renderExpirationTime) { pushHostContext(workInProgress); if (current === null) { tryToClaimNextHydratableInstance(workInProgress); } var type = workInProgress.type; var memoizedProps = workInProgress.memoizedProps; var nextProps = workInProgress.pendingProps; if (nextProps === null) { nextProps = memoizedProps; !(nextProps !== null) ? invariant(false, 'We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue.') : void 0; } var prevProps = current !== null ? current.memoizedProps : null; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. } else if (nextProps === null || memoizedProps === nextProps) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } var nextChildren = nextProps.children; var isDirectTextChild = shouldSetTextContent(type, nextProps); if (isDirectTextChild) { // We special case a direct text child of a host node. This is a common // case. We won't handle it as a reified child. We will instead handle // this in the host environment that also have access to this prop. That // avoids allocating another HostText fiber and traversing it. nextChildren = null; } else if (prevProps && shouldSetTextContent(type, prevProps)) { // If we're switching from a direct text child to a normal child, or to // empty, we need to schedule the text content to be reset. workInProgress.effectTag |= ContentReset; } markRef(current, workInProgress); // Check the host config to see if the children are offscreen/hidden. if (renderExpirationTime !== Never && !useSyncScheduling && shouldDeprioritizeSubtree(type, nextProps)) { // Down-prioritize the children. workInProgress.expirationTime = Never; // Bailout and come back to this fiber later. return null; } reconcileChildren(current, workInProgress, nextChildren); memoizeProps(workInProgress, nextProps); return workInProgress.child; } function updateHostText(current, workInProgress) { if (current === null) { tryToClaimNextHydratableInstance(workInProgress); } var nextProps = workInProgress.pendingProps; if (nextProps === null) { nextProps = workInProgress.memoizedProps; } memoizeProps(workInProgress, nextProps); // Nothing to do here. This is terminal. We'll do the completion step // immediately after. return null; } function mountIndeterminateComponent(current, workInProgress, renderExpirationTime) { !(current === null) ? invariant(false, 'An indeterminate component should never have mounted. This error is likely caused by a bug in React. Please file an issue.') : void 0; var fn = workInProgress.type; var props = workInProgress.pendingProps; var unmaskedContext = getUnmaskedContext(workInProgress); var context = getMaskedContext(workInProgress, unmaskedContext); var value; { if (fn.prototype && typeof fn.prototype.render === 'function') { var componentName = getComponentName(workInProgress); warning(false, "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + 'This is likely to cause errors. Change %s to extend React.Component instead.', componentName, componentName); } ReactCurrentOwner.current = workInProgress; value = fn(props, context); } // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; if (typeof value === 'object' && value !== null && typeof value.render === 'function') { // Proceed under the assumption that this is a class instance workInProgress.tag = ClassComponent; // Push context providers early to prevent context stack mismatches. // During mounting we don't know the child context yet as the instance doesn't exist. // We will invalidate the child context in finishClassComponent() right after rendering. var hasContext = pushContextProvider(workInProgress); adoptClassInstance(workInProgress, value); mountClassInstance(workInProgress, renderExpirationTime); return finishClassComponent(current, workInProgress, true, hasContext); } else { // Proceed under the assumption that this is a functional component workInProgress.tag = FunctionalComponent; { var Component = workInProgress.type; if (Component) { warning(!Component.childContextTypes, '%s(...): childContextTypes cannot be defined on a functional component.', Component.displayName || Component.name || 'Component'); } if (workInProgress.ref !== null) { var info = ''; var ownerName = ReactDebugCurrentFiber.getCurrentFiberOwnerName(); if (ownerName) { info += '\n\nCheck the render method of `' + ownerName + '`.'; } var warningKey = ownerName || workInProgress._debugID || ''; var debugSource = workInProgress._debugSource; if (debugSource) { warningKey = debugSource.fileName + ':' + debugSource.lineNumber; } if (!warnedAboutStatelessRefs[warningKey]) { warnedAboutStatelessRefs[warningKey] = true; warning(false, 'Stateless function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s%s', info, ReactDebugCurrentFiber.getCurrentFiberStackAddendum()); } } } reconcileChildren(current, workInProgress, value); memoizeProps(workInProgress, props); return workInProgress.child; } } function updateCallComponent(current, workInProgress, renderExpirationTime) { var nextCall = workInProgress.pendingProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. if (nextCall === null) { nextCall = current && current.memoizedProps; !(nextCall !== null) ? invariant(false, 'We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue.') : void 0; } } else if (nextCall === null || workInProgress.memoizedProps === nextCall) { nextCall = workInProgress.memoizedProps; // TODO: When bailing out, we might need to return the stateNode instead // of the child. To check it for work. // return bailoutOnAlreadyFinishedWork(current, workInProgress); } var nextChildren = nextCall.children; // The following is a fork of reconcileChildrenAtExpirationTime but using // stateNode to store the child. if (current === null) { workInProgress.stateNode = mountChildFibers(workInProgress, workInProgress.stateNode, nextChildren, renderExpirationTime); } else { workInProgress.stateNode = reconcileChildFibers(workInProgress, workInProgress.stateNode, nextChildren, renderExpirationTime); } memoizeProps(workInProgress, nextCall); // This doesn't take arbitrary time so we could synchronously just begin // eagerly do the work of workInProgress.child as an optimization. return workInProgress.stateNode; } function updatePortalComponent(current, workInProgress, renderExpirationTime) { pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); var nextChildren = workInProgress.pendingProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. if (nextChildren === null) { nextChildren = current && current.memoizedProps; !(nextChildren != null) ? invariant(false, 'We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue.') : void 0; } } else if (nextChildren === null || workInProgress.memoizedProps === nextChildren) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } if (current === null) { // Portals are special because we don't append the children during mount // but at commit. Therefore we need to track insertions which the normal // flow doesn't do during mount. This doesn't happen at the root because // the root always starts with a "current" with a null child. // TODO: Consider unifying this with how the root works. workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime); memoizeProps(workInProgress, nextChildren); } else { reconcileChildren(current, workInProgress, nextChildren); memoizeProps(workInProgress, nextChildren); } return workInProgress.child; } /* function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) { let child = firstChild; do { // Ensure that the first and last effect of the parent corresponds // to the children's first and last effect. if (!returnFiber.firstEffect) { returnFiber.firstEffect = child.firstEffect; } if (child.lastEffect) { if (returnFiber.lastEffect) { returnFiber.lastEffect.nextEffect = child.firstEffect; } returnFiber.lastEffect = child.lastEffect; } } while (child = child.sibling); } */ function bailoutOnAlreadyFinishedWork(current, workInProgress) { cancelWorkTimer(workInProgress); // TODO: We should ideally be able to bail out early if the children have no // more work to do. However, since we don't have a separation of this // Fiber's priority and its children yet - we don't know without doing lots // of the same work we do anyway. Once we have that separation we can just // bail out here if the children has no more work at this priority level. // if (workInProgress.priorityOfChildren <= priorityLevel) { // // If there are side-effects in these children that have not yet been // // committed we need to ensure that they get properly transferred up. // if (current && current.child !== workInProgress.child) { // reuseChildrenEffects(workInProgress, child); // } // return null; // } cloneChildFibers(current, workInProgress); return workInProgress.child; } function bailoutOnLowPriority(current, workInProgress) { cancelWorkTimer(workInProgress); // TODO: Handle HostComponent tags here as well and call pushHostContext()? // See PR 8590 discussion for context switch (workInProgress.tag) { case HostRoot: pushHostRootContext(workInProgress); break; case ClassComponent: pushContextProvider(workInProgress); break; case HostPortal: pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); break; } // TODO: What if this is currently in progress? // How can that happen? How is this not being cloned? return null; } // TODO: Delete memoizeProps/State and move to reconcile/bailout instead function memoizeProps(workInProgress, nextProps) { workInProgress.memoizedProps = nextProps; } function memoizeState(workInProgress, nextState) { workInProgress.memoizedState = nextState; // Don't reset the updateQueue, in case there are pending updates. Resetting // is handled by processUpdateQueue. } function beginWork(current, workInProgress, renderExpirationTime) { if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) { return bailoutOnLowPriority(current, workInProgress); } switch (workInProgress.tag) { case IndeterminateComponent: return mountIndeterminateComponent(current, workInProgress, renderExpirationTime); case FunctionalComponent: return updateFunctionalComponent(current, workInProgress); case ClassComponent: return updateClassComponent(current, workInProgress, renderExpirationTime); case HostRoot: return updateHostRoot(current, workInProgress, renderExpirationTime); case HostComponent: return updateHostComponent(current, workInProgress, renderExpirationTime); case HostText: return updateHostText(current, workInProgress); case CallHandlerPhase: // This is a restart. Reset the tag to the initial phase. workInProgress.tag = CallComponent; // Intentionally fall through since this is now the same. case CallComponent: return updateCallComponent(current, workInProgress, renderExpirationTime); case ReturnComponent: // A return component is just a placeholder, we can just run through the // next one immediately. return null; case HostPortal: return updatePortalComponent(current, workInProgress, renderExpirationTime); case Fragment: return updateFragment(current, workInProgress); default: invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.'); } } function beginFailedWork(current, workInProgress, renderExpirationTime) { // Push context providers here to avoid a push/pop context mismatch. switch (workInProgress.tag) { case ClassComponent: pushContextProvider(workInProgress); break; case HostRoot: pushHostRootContext(workInProgress); break; default: invariant(false, 'Invalid type of work. This error is likely caused by a bug in React. Please file an issue.'); } // Add an error effect so we can handle the error during the commit phase workInProgress.effectTag |= Err; // This is a weird case where we do "resume" work — work that failed on // our first attempt. Because we no longer have a notion of "progressed // deletions," reset the child to the current child to make sure we delete // it again. TODO: Find a better way to handle this, perhaps during a more // general overhaul of error handling. if (current === null) { workInProgress.child = null; } else if (workInProgress.child !== current.child) { workInProgress.child = current.child; } if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) { return bailoutOnLowPriority(current, workInProgress); } // If we don't bail out, we're going be recomputing our children so we need // to drop our effect list. workInProgress.firstEffect = null; workInProgress.lastEffect = null; // Unmount the current children as if the component rendered null var nextChildren = null; reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, renderExpirationTime); if (workInProgress.tag === ClassComponent) { var instance = workInProgress.stateNode; workInProgress.memoizedProps = instance.props; workInProgress.memoizedState = instance.state; } return workInProgress.child; } return { beginWork: beginWork, beginFailedWork: beginFailedWork }; }; var ReactFiberCompleteWork = function (config, hostContext, hydrationContext) { var createInstance = config.createInstance, createTextInstance = config.createTextInstance, appendInitialChild = config.appendInitialChild, finalizeInitialChildren = config.finalizeInitialChildren, prepareUpdate = config.prepareUpdate, mutation = config.mutation, persistence = config.persistence; var getRootHostContainer = hostContext.getRootHostContainer, popHostContext = hostContext.popHostContext, getHostContext = hostContext.getHostContext, popHostContainer = hostContext.popHostContainer; var prepareToHydrateHostInstance = hydrationContext.prepareToHydrateHostInstance, prepareToHydrateHostTextInstance = hydrationContext.prepareToHydrateHostTextInstance, popHydrationState = hydrationContext.popHydrationState; function markUpdate(workInProgress) { // Tag the fiber with an update effect. This turns a Placement into // an UpdateAndPlacement. workInProgress.effectTag |= Update; } function markRef(workInProgress) { workInProgress.effectTag |= Ref; } function appendAllReturns(returns, workInProgress) { var node = workInProgress.stateNode; if (node) { node['return'] = workInProgress; } while (node !== null) { if (node.tag === HostComponent || node.tag === HostText || node.tag === HostPortal) { invariant(false, 'A call cannot have host component children.'); } else if (node.tag === ReturnComponent) { returns.push(node.type); } else if (node.child !== null) { node.child['return'] = node; node = node.child; continue; } while (node.sibling === null) { if (node['return'] === null || node['return'] === workInProgress) { return; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; } } function moveCallToHandlerPhase(current, workInProgress, renderExpirationTime) { var call = workInProgress.memoizedProps; !call ? invariant(false, 'Should be resolved by now. This error is likely caused by a bug in React. Please file an issue.') : void 0; // First step of the call has completed. Now we need to do the second. // TODO: It would be nice to have a multi stage call represented by a // single component, or at least tail call optimize nested ones. Currently // that requires additional fields that we don't want to add to the fiber. // So this requires nested handlers. // Note: This doesn't mutate the alternate node. I don't think it needs to // since this stage is reset for every pass. workInProgress.tag = CallHandlerPhase; // Build up the returns. // TODO: Compare this to a generator or opaque helpers like Children. var returns = []; appendAllReturns(returns, workInProgress); var fn = call.handler; var props = call.props; var nextChildren = fn(props, returns); var currentFirstChild = current !== null ? current.child : null; workInProgress.child = reconcileChildFibers(workInProgress, currentFirstChild, nextChildren, renderExpirationTime); return workInProgress.child; } function appendAllChildren(parent, workInProgress) { // We only have the top Fiber that was created but we need recurse down its // children to find all the terminal nodes. var node = workInProgress.child; while (node !== null) { if (node.tag === HostComponent || node.tag === HostText) { appendInitialChild(parent, node.stateNode); } else if (node.tag === HostPortal) { // If we have a portal child, then we don't want to traverse // down its children. Instead, we'll get insertions from each child in // the portal directly. } else if (node.child !== null) { node.child['return'] = node; node = node.child; continue; } if (node === workInProgress) { return; } while (node.sibling === null) { if (node['return'] === null || node['return'] === workInProgress) { return; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; } } var updateHostContainer = void 0; var updateHostComponent = void 0; var updateHostText = void 0; if (mutation) { if (enableMutatingReconciler) { // Mutation mode updateHostContainer = function (workInProgress) { // Noop }; updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) { // TODO: Type this specific to this type of component. workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there // is a new ref we mark this as an update. All the work is done in commitWork. if (updatePayload) { markUpdate(workInProgress); } }; updateHostText = function (current, workInProgress, oldText, newText) { // If the text differs, mark it as an update. All the work in done in commitWork. if (oldText !== newText) { markUpdate(workInProgress); } }; } else { invariant(false, 'Mutating reconciler is disabled.'); } } else if (persistence) { if (enablePersistentReconciler) { // Persistent host tree mode var cloneInstance = persistence.cloneInstance, createContainerChildSet = persistence.createContainerChildSet, appendChildToContainerChildSet = persistence.appendChildToContainerChildSet, finalizeContainerChildren = persistence.finalizeContainerChildren; // An unfortunate fork of appendAllChildren because we have two different parent types. var appendAllChildrenToContainer = function (containerChildSet, workInProgress) { // We only have the top Fiber that was created but we need recurse down its // children to find all the terminal nodes. var node = workInProgress.child; while (node !== null) { if (node.tag === HostComponent || node.tag === HostText) { appendChildToContainerChildSet(containerChildSet, node.stateNode); } else if (node.tag === HostPortal) { // If we have a portal child, then we don't want to traverse // down its children. Instead, we'll get insertions from each child in // the portal directly. } else if (node.child !== null) { node.child['return'] = node; node = node.child; continue; } if (node === workInProgress) { return; } while (node.sibling === null) { if (node['return'] === null || node['return'] === workInProgress) { return; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; } }; updateHostContainer = function (workInProgress) { var portalOrRoot = workInProgress.stateNode; var childrenUnchanged = workInProgress.firstEffect === null; if (childrenUnchanged) { // No changes, just reuse the existing instance. } else { var container = portalOrRoot.containerInfo; var newChildSet = createContainerChildSet(container); if (finalizeContainerChildren(container, newChildSet)) { markUpdate(workInProgress); } portalOrRoot.pendingChildren = newChildSet; // If children might have changed, we have to add them all to the set. appendAllChildrenToContainer(newChildSet, workInProgress); // Schedule an update on the container to swap out the container. markUpdate(workInProgress); } }; updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) { // If there are no effects associated with this node, then none of our children had any updates. // This guarantees that we can reuse all of them. var childrenUnchanged = workInProgress.firstEffect === null; var currentInstance = current.stateNode; if (childrenUnchanged && updatePayload === null) { // No changes, just reuse the existing instance. // Note that this might release a previous clone. workInProgress.stateNode = currentInstance; } else { var recyclableInstance = workInProgress.stateNode; var newInstance = cloneInstance(currentInstance, updatePayload, type, oldProps, newProps, workInProgress, childrenUnchanged, recyclableInstance); if (finalizeInitialChildren(newInstance, type, newProps, rootContainerInstance)) { markUpdate(workInProgress); } workInProgress.stateNode = newInstance; if (childrenUnchanged) { // If there are no other effects in this tree, we need to flag this node as having one. // Even though we're not going to use it for anything. // Otherwise parents won't know that there are new children to propagate upwards. markUpdate(workInProgress); } else { // If children might have changed, we have to add them all to the set. appendAllChildren(newInstance, workInProgress); } } }; updateHostText = function (current, workInProgress, oldText, newText) { if (oldText !== newText) { // If the text content differs, we'll create a new text instance for it. var rootContainerInstance = getRootHostContainer(); var currentHostContext = getHostContext(); workInProgress.stateNode = createTextInstance(newText, rootContainerInstance, currentHostContext, workInProgress); // We'll have to mark it as having an effect, even though we won't use the effect for anything. // This lets the parents know that at least one of their children has changed. markUpdate(workInProgress); } }; } else { invariant(false, 'Persistent reconciler is disabled.'); } } else { if (enableNoopReconciler) { // No host operations updateHostContainer = function (workInProgress) { // Noop }; updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) { // Noop }; updateHostText = function (current, workInProgress, oldText, newText) { // Noop }; } else { invariant(false, 'Noop reconciler is disabled.'); } } function completeWork(current, workInProgress, renderExpirationTime) { // Get the latest props. var newProps = workInProgress.pendingProps; if (newProps === null) { newProps = workInProgress.memoizedProps; } else if (workInProgress.expirationTime !== Never || renderExpirationTime === Never) { // Reset the pending props, unless this was a down-prioritization. workInProgress.pendingProps = null; } switch (workInProgress.tag) { case FunctionalComponent: return null; case ClassComponent: { // We are leaving this subtree, so pop context if any. popContextProvider(workInProgress); return null; } case HostRoot: { popHostContainer(workInProgress); popTopLevelContextObject(workInProgress); var fiberRoot = workInProgress.stateNode; if (fiberRoot.pendingContext) { fiberRoot.context = fiberRoot.pendingContext; fiberRoot.pendingContext = null; } if (current === null || current.child === null) { // If we hydrated, pop so that we can delete any remaining children // that weren't hydrated. popHydrationState(workInProgress); // This resets the hacky state to fix isMounted before committing. // TODO: Delete this when we delete isMounted and findDOMNode. workInProgress.effectTag &= ~Placement; } updateHostContainer(workInProgress); return null; } case HostComponent: { popHostContext(workInProgress); var rootContainerInstance = getRootHostContainer(); var type = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { // If we have an alternate, that means this is an update and we need to // schedule a side-effect to do the updates. var oldProps = current.memoizedProps; // If we get updated because one of our children updated, we don't // have newProps so we'll have to reuse them. // TODO: Split the update API as separate for the props vs. children. // Even better would be if children weren't special cased at all tho. var instance = workInProgress.stateNode; var currentHostContext = getHostContext(); var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext); updateHostComponent(current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance); if (current.ref !== workInProgress.ref) { markRef(workInProgress); } } else { if (!newProps) { !(workInProgress.stateNode !== null) ? invariant(false, 'We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue.') : void 0; // This can happen when we abort work. return null; } var _currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context // "stack" as the parent. Then append children as we go in beginWork // or completeWork depending on we want to add then top->down or // bottom->up. Top->down is faster in IE11. var wasHydrated = popHydrationState(workInProgress); if (wasHydrated) { // TODO: Move this and createInstance step into the beginPhase // to consolidate. if (prepareToHydrateHostInstance(workInProgress, rootContainerInstance, _currentHostContext)) { // If changes to the hydrated node needs to be applied at the // commit-phase we mark this as such. markUpdate(workInProgress); } } else { var _instance = createInstance(type, newProps, rootContainerInstance, _currentHostContext, workInProgress); appendAllChildren(_instance, workInProgress); // Certain renderers require commit-time effects for initial mount. // (eg DOM renderer supports auto-focus for certain elements). // Make sure such renderers get scheduled for later work. if (finalizeInitialChildren(_instance, type, newProps, rootContainerInstance)) { markUpdate(workInProgress); } workInProgress.stateNode = _instance; } if (workInProgress.ref !== null) { // If there is a ref on a host node we need to schedule a callback markRef(workInProgress); } } return null; } case HostText: { var newText = newProps; if (current && workInProgress.stateNode != null) { var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need // to schedule a side-effect to do the updates. updateHostText(current, workInProgress, oldText, newText); } else { if (typeof newText !== 'string') { !(workInProgress.stateNode !== null) ? invariant(false, 'We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue.') : void 0; // This can happen when we abort work. return null; } var _rootContainerInstance = getRootHostContainer(); var _currentHostContext2 = getHostContext(); var _wasHydrated = popHydrationState(workInProgress); if (_wasHydrated) { if (prepareToHydrateHostTextInstance(workInProgress)) { markUpdate(workInProgress); } } else { workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext2, workInProgress); } } return null; } case CallComponent: return moveCallToHandlerPhase(current, workInProgress, renderExpirationTime); case CallHandlerPhase: // Reset the tag to now be a first phase call. workInProgress.tag = CallComponent; return null; case ReturnComponent: // Does nothing. return null; case Fragment: return null; case HostPortal: popHostContainer(workInProgress); updateHostContainer(workInProgress); return null; // Error cases case IndeterminateComponent: invariant(false, 'An indeterminate component should have become determinate before completing. This error is likely caused by a bug in React. Please file an issue.'); // eslint-disable-next-line no-fallthrough default: invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.'); } } return { completeWork: completeWork }; }; var invokeGuardedCallback$2 = ReactErrorUtils.invokeGuardedCallback; var hasCaughtError$1 = ReactErrorUtils.hasCaughtError; var clearCaughtError$1 = ReactErrorUtils.clearCaughtError; var ReactFiberCommitWork = function (config, captureError) { var getPublicInstance = config.getPublicInstance, mutation = config.mutation, persistence = config.persistence; var callComponentWillUnmountWithTimer = function (current, instance) { startPhaseTimer(current, 'componentWillUnmount'); instance.props = current.memoizedProps; instance.state = current.memoizedState; instance.componentWillUnmount(); stopPhaseTimer(); }; // Capture errors so they don't interrupt unmounting. function safelyCallComponentWillUnmount(current, instance) { { invokeGuardedCallback$2(null, callComponentWillUnmountWithTimer, null, current, instance); if (hasCaughtError$1()) { var unmountError = clearCaughtError$1(); captureError(current, unmountError); } } } function safelyDetachRef(current) { var ref = current.ref; if (ref !== null) { { invokeGuardedCallback$2(null, ref, null, null); if (hasCaughtError$1()) { var refError = clearCaughtError$1(); captureError(current, refError); } } } } function commitLifeCycles(current, finishedWork) { switch (finishedWork.tag) { case ClassComponent: { var instance = finishedWork.stateNode; if (finishedWork.effectTag & Update) { if (current === null) { startPhaseTimer(finishedWork, 'componentDidMount'); instance.props = finishedWork.memoizedProps; instance.state = finishedWork.memoizedState; instance.componentDidMount(); stopPhaseTimer(); } else { var prevProps = current.memoizedProps; var prevState = current.memoizedState; startPhaseTimer(finishedWork, 'componentDidUpdate'); instance.props = finishedWork.memoizedProps; instance.state = finishedWork.memoizedState; instance.componentDidUpdate(prevProps, prevState); stopPhaseTimer(); } } var updateQueue = finishedWork.updateQueue; if (updateQueue !== null) { commitCallbacks(updateQueue, instance); } return; } case HostRoot: { var _updateQueue = finishedWork.updateQueue; if (_updateQueue !== null) { var _instance = finishedWork.child !== null ? finishedWork.child.stateNode : null; commitCallbacks(_updateQueue, _instance); } return; } case HostComponent: { var _instance2 = finishedWork.stateNode; // Renderers may schedule work to be done after host components are mounted // (eg DOM renderer may schedule auto-focus for inputs and form controls). // These effects should only be committed when components are first mounted, // aka when there is no current/alternate. if (current === null && finishedWork.effectTag & Update) { var type = finishedWork.type; var props = finishedWork.memoizedProps; commitMount(_instance2, type, props, finishedWork); } return; } case HostText: { // We have no life-cycles associated with text. return; } case HostPortal: { // We have no life-cycles associated with portals. return; } default: { invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.'); } } } function commitAttachRef(finishedWork) { var ref = finishedWork.ref; if (ref !== null) { var instance = finishedWork.stateNode; switch (finishedWork.tag) { case HostComponent: ref(getPublicInstance(instance)); break; default: ref(instance); } } } function commitDetachRef(current) { var currentRef = current.ref; if (currentRef !== null) { currentRef(null); } } // User-originating errors (lifecycles and refs) should not interrupt // deletion, so don't let them throw. Host-originating errors should // interrupt deletion, so it's okay function commitUnmount(current) { if (typeof onCommitUnmount === 'function') { onCommitUnmount(current); } switch (current.tag) { case ClassComponent: { safelyDetachRef(current); var instance = current.stateNode; if (typeof instance.componentWillUnmount === 'function') { safelyCallComponentWillUnmount(current, instance); } return; } case HostComponent: { safelyDetachRef(current); return; } case CallComponent: { commitNestedUnmounts(current.stateNode); return; } case HostPortal: { // TODO: this is recursive. // We are also not using this parent because // the portal will get pushed immediately. if (enableMutatingReconciler && mutation) { unmountHostComponents(current); } else if (enablePersistentReconciler && persistence) { emptyPortalContainer(current); } return; } } } function commitNestedUnmounts(root) { // While we're inside a removed host node we don't want to call // removeChild on the inner nodes because they're removed by the top // call anyway. We also want to call componentWillUnmount on all // composites before this host node is removed from the tree. Therefore var node = root; while (true) { commitUnmount(node); // Visit children because they may contain more composite or host nodes. // Skip portals because commitUnmount() currently visits them recursively. if (node.child !== null && ( // If we use mutation we drill down into portals using commitUnmount above. // If we don't use mutation we drill down into portals here instead. !mutation || node.tag !== HostPortal)) { node.child['return'] = node; node = node.child; continue; } if (node === root) { return; } while (node.sibling === null) { if (node['return'] === null || node['return'] === root) { return; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; } } function detachFiber(current) { // Cut off the return pointers to disconnect it from the tree. Ideally, we // should clear the child pointer of the parent alternate to let this // get GC:ed but we don't know which for sure which parent is the current // one so we'll settle for GC:ing the subtree of this child. This child // itself will be GC:ed when the parent updates the next time. current['return'] = null; current.child = null; if (current.alternate) { current.alternate.child = null; current.alternate['return'] = null; } } if (!mutation) { var commitContainer = void 0; if (persistence) { var replaceContainerChildren = persistence.replaceContainerChildren, createContainerChildSet = persistence.createContainerChildSet; var emptyPortalContainer = function (current) { var portal = current.stateNode; var containerInfo = portal.containerInfo; var emptyChildSet = createContainerChildSet(containerInfo); replaceContainerChildren(containerInfo, emptyChildSet); }; commitContainer = function (finishedWork) { switch (finishedWork.tag) { case ClassComponent: { return; } case HostComponent: { return; } case HostText: { return; } case HostRoot: case HostPortal: { var portalOrRoot = finishedWork.stateNode; var containerInfo = portalOrRoot.containerInfo, _pendingChildren = portalOrRoot.pendingChildren; replaceContainerChildren(containerInfo, _pendingChildren); return; } default: { invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.'); } } }; } else { commitContainer = function (finishedWork) { // Noop }; } if (enablePersistentReconciler || enableNoopReconciler) { return { commitResetTextContent: function (finishedWork) {}, commitPlacement: function (finishedWork) {}, commitDeletion: function (current) { // Detach refs and call componentWillUnmount() on the whole subtree. commitNestedUnmounts(current); detachFiber(current); }, commitWork: function (current, finishedWork) { commitContainer(finishedWork); }, commitLifeCycles: commitLifeCycles, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; } else if (persistence) { invariant(false, 'Persistent reconciler is disabled.'); } else { invariant(false, 'Noop reconciler is disabled.'); } } var commitMount = mutation.commitMount, commitUpdate = mutation.commitUpdate, resetTextContent = mutation.resetTextContent, commitTextUpdate = mutation.commitTextUpdate, appendChild = mutation.appendChild, appendChildToContainer = mutation.appendChildToContainer, insertBefore = mutation.insertBefore, insertInContainerBefore = mutation.insertInContainerBefore, removeChild = mutation.removeChild, removeChildFromContainer = mutation.removeChildFromContainer; function getHostParentFiber(fiber) { var parent = fiber['return']; while (parent !== null) { if (isHostParent(parent)) { return parent; } parent = parent['return']; } invariant(false, 'Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue.'); } function isHostParent(fiber) { return fiber.tag === HostComponent || fiber.tag === HostRoot || fiber.tag === HostPortal; } function getHostSibling(fiber) { // We're going to search forward into the tree until we find a sibling host // node. Unfortunately, if multiple insertions are done in a row we have to // search past them. This leads to exponential search for the next sibling. var node = fiber; siblings: while (true) { // If we didn't find anything, let's try the next sibling. while (node.sibling === null) { if (node['return'] === null || isHostParent(node['return'])) { // If we pop out of the root or hit the parent the fiber we are the // last sibling. return null; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; while (node.tag !== HostComponent && node.tag !== HostText) { // If it is not host node and, we might have a host node inside it. // Try to search down until we find one. if (node.effectTag & Placement) { // If we don't have a child, try the siblings instead. continue siblings; } // If we don't have a child, try the siblings instead. // We also skip portals because they are not part of this host tree. if (node.child === null || node.tag === HostPortal) { continue siblings; } else { node.child['return'] = node; node = node.child; } } // Check if this host node is stable or about to be placed. if (!(node.effectTag & Placement)) { // Found it! return node.stateNode; } } } function commitPlacement(finishedWork) { // Recursively insert all host nodes into the parent. var parentFiber = getHostParentFiber(finishedWork); var parent = void 0; var isContainer = void 0; switch (parentFiber.tag) { case HostComponent: parent = parentFiber.stateNode; isContainer = false; break; case HostRoot: parent = parentFiber.stateNode.containerInfo; isContainer = true; break; case HostPortal: parent = parentFiber.stateNode.containerInfo; isContainer = true; break; default: invariant(false, 'Invalid host parent fiber. This error is likely caused by a bug in React. Please file an issue.'); } if (parentFiber.effectTag & ContentReset) { // Reset the text content of the parent before doing any insertions resetTextContent(parent); // Clear ContentReset from the effect tag parentFiber.effectTag &= ~ContentReset; } var before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need recurse down its // children to find all the terminal nodes. var node = finishedWork; while (true) { if (node.tag === HostComponent || node.tag === HostText) { if (before) { if (isContainer) { insertInContainerBefore(parent, node.stateNode, before); } else { insertBefore(parent, node.stateNode, before); } } else { if (isContainer) { appendChildToContainer(parent, node.stateNode); } else { appendChild(parent, node.stateNode); } } } else if (node.tag === HostPortal) { // If the insertion itself is a portal, then we don't want to traverse // down its children. Instead, we'll get insertions from each child in // the portal directly. } else if (node.child !== null) { node.child['return'] = node; node = node.child; continue; } if (node === finishedWork) { return; } while (node.sibling === null) { if (node['return'] === null || node['return'] === finishedWork) { return; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; } } function unmountHostComponents(current) { // We only have the top Fiber that was inserted but we need recurse down its var node = current; // Each iteration, currentParent is populated with node's host parent if not // currentParentIsValid. var currentParentIsValid = false; var currentParent = void 0; var currentParentIsContainer = void 0; while (true) { if (!currentParentIsValid) { var parent = node['return']; findParent: while (true) { !(parent !== null) ? invariant(false, 'Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue.') : void 0; switch (parent.tag) { case HostComponent: currentParent = parent.stateNode; currentParentIsContainer = false; break findParent; case HostRoot: currentParent = parent.stateNode.containerInfo; currentParentIsContainer = true; break findParent; case HostPortal: currentParent = parent.stateNode.containerInfo; currentParentIsContainer = true; break findParent; } parent = parent['return']; } currentParentIsValid = true; } if (node.tag === HostComponent || node.tag === HostText) { commitNestedUnmounts(node); // After all the children have unmounted, it is now safe to remove the // node from the tree. if (currentParentIsContainer) { removeChildFromContainer(currentParent, node.stateNode); } else { removeChild(currentParent, node.stateNode); } // Don't visit children because we already visited them. } else if (node.tag === HostPortal) { // When we go into a portal, it becomes the parent to remove from. // We will reassign it back when we pop the portal on the way up. currentParent = node.stateNode.containerInfo; // Visit children because portals might contain host components. if (node.child !== null) { node.child['return'] = node; node = node.child; continue; } } else { commitUnmount(node); // Visit children because we may find more host components below. if (node.child !== null) { node.child['return'] = node; node = node.child; continue; } } if (node === current) { return; } while (node.sibling === null) { if (node['return'] === null || node['return'] === current) { return; } node = node['return']; if (node.tag === HostPortal) { // When we go out of the portal, we need to restore the parent. // Since we don't keep a stack of them, we will search for it. currentParentIsValid = false; } } node.sibling['return'] = node['return']; node = node.sibling; } } function commitDeletion(current) { // Recursively delete all host nodes from the parent. // Detach refs and call componentWillUnmount() on the whole subtree. unmountHostComponents(current); detachFiber(current); } function commitWork(current, finishedWork) { switch (finishedWork.tag) { case ClassComponent: { return; } case HostComponent: { var instance = finishedWork.stateNode; if (instance != null) { // Commit the work prepared earlier. var newProps = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps // as the newProps. The updatePayload will contain the real change in // this case. var oldProps = current !== null ? current.memoizedProps : newProps; var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components. var updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; if (updatePayload !== null) { commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork); } } return; } case HostText: { !(finishedWork.stateNode !== null) ? invariant(false, 'This should have a text node initialized. This error is likely caused by a bug in React. Please file an issue.') : void 0; var textInstance = finishedWork.stateNode; var newText = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps // as the newProps. The updatePayload will contain the real change in // this case. var oldText = current !== null ? current.memoizedProps : newText; commitTextUpdate(textInstance, oldText, newText); return; } case HostRoot: { return; } default: { invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.'); } } } function commitResetTextContent(current) { resetTextContent(current.stateNode); } if (enableMutatingReconciler) { return { commitResetTextContent: commitResetTextContent, commitPlacement: commitPlacement, commitDeletion: commitDeletion, commitWork: commitWork, commitLifeCycles: commitLifeCycles, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; } else { invariant(false, 'Mutating reconciler is disabled.'); } }; var NO_CONTEXT = {}; var ReactFiberHostContext = function (config) { var getChildHostContext = config.getChildHostContext, getRootHostContext = config.getRootHostContext; var contextStackCursor = createCursor(NO_CONTEXT); var contextFiberStackCursor = createCursor(NO_CONTEXT); var rootInstanceStackCursor = createCursor(NO_CONTEXT); function requiredContext(c) { !(c !== NO_CONTEXT) ? invariant(false, 'Expected host context to exist. This error is likely caused by a bug in React. Please file an issue.') : void 0; return c; } function getRootHostContainer() { var rootInstance = requiredContext(rootInstanceStackCursor.current); return rootInstance; } function pushHostContainer(fiber, nextRootInstance) { // Push current root instance onto the stack; // This allows us to reset root when portals are popped. push(rootInstanceStackCursor, nextRootInstance, fiber); var nextRootContext = getRootHostContext(nextRootInstance); // Track the context and the Fiber that provided it. // This enables us to pop only Fibers that provide unique contexts. push(contextFiberStackCursor, fiber, fiber); push(contextStackCursor, nextRootContext, fiber); } function popHostContainer(fiber) { pop(contextStackCursor, fiber); pop(contextFiberStackCursor, fiber); pop(rootInstanceStackCursor, fiber); } function getHostContext() { var context = requiredContext(contextStackCursor.current); return context; } function pushHostContext(fiber) { var rootInstance = requiredContext(rootInstanceStackCursor.current); var context = requiredContext(contextStackCursor.current); var nextContext = getChildHostContext(context, fiber.type, rootInstance); // Don't push this Fiber's context unless it's unique. if (context === nextContext) { return; } // Track the context and the Fiber that provided it. // This enables us to pop only Fibers that provide unique contexts. push(contextFiberStackCursor, fiber, fiber); push(contextStackCursor, nextContext, fiber); } function popHostContext(fiber) { // Do not pop unless this Fiber provided the current context. // pushHostContext() only pushes Fibers that provide unique contexts. if (contextFiberStackCursor.current !== fiber) { return; } pop(contextStackCursor, fiber); pop(contextFiberStackCursor, fiber); } function resetHostContainer() { contextStackCursor.current = NO_CONTEXT; rootInstanceStackCursor.current = NO_CONTEXT; } return { getHostContext: getHostContext, getRootHostContainer: getRootHostContainer, popHostContainer: popHostContainer, popHostContext: popHostContext, pushHostContainer: pushHostContainer, pushHostContext: pushHostContext, resetHostContainer: resetHostContainer }; }; var ReactFiberHydrationContext = function (config) { var shouldSetTextContent = config.shouldSetTextContent, hydration = config.hydration; // If this doesn't have hydration mode. if (!hydration) { return { enterHydrationState: function () { return false; }, resetHydrationState: function () {}, tryToClaimNextHydratableInstance: function () {}, prepareToHydrateHostInstance: function () { invariant(false, 'Expected prepareToHydrateHostInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.'); }, prepareToHydrateHostTextInstance: function () { invariant(false, 'Expected prepareToHydrateHostTextInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.'); }, popHydrationState: function (fiber) { return false; } }; } var canHydrateInstance = hydration.canHydrateInstance, canHydrateTextInstance = hydration.canHydrateTextInstance, getNextHydratableSibling = hydration.getNextHydratableSibling, getFirstHydratableChild = hydration.getFirstHydratableChild, hydrateInstance = hydration.hydrateInstance, hydrateTextInstance = hydration.hydrateTextInstance, didNotMatchHydratedContainerTextInstance = hydration.didNotMatchHydratedContainerTextInstance, didNotMatchHydratedTextInstance = hydration.didNotMatchHydratedTextInstance, didNotHydrateContainerInstance = hydration.didNotHydrateContainerInstance, didNotHydrateInstance = hydration.didNotHydrateInstance, didNotFindHydratableContainerInstance = hydration.didNotFindHydratableContainerInstance, didNotFindHydratableContainerTextInstance = hydration.didNotFindHydratableContainerTextInstance, didNotFindHydratableInstance = hydration.didNotFindHydratableInstance, didNotFindHydratableTextInstance = hydration.didNotFindHydratableTextInstance; // The deepest Fiber on the stack involved in a hydration context. // This may have been an insertion or a hydration. var hydrationParentFiber = null; var nextHydratableInstance = null; var isHydrating = false; function enterHydrationState(fiber) { var parentInstance = fiber.stateNode.containerInfo; nextHydratableInstance = getFirstHydratableChild(parentInstance); hydrationParentFiber = fiber; isHydrating = true; return true; } function deleteHydratableInstance(returnFiber, instance) { { switch (returnFiber.tag) { case HostRoot: didNotHydrateContainerInstance(returnFiber.stateNode.containerInfo, instance); break; case HostComponent: didNotHydrateInstance(returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance); break; } } var childToDelete = createFiberFromHostInstanceForDeletion(); childToDelete.stateNode = instance; childToDelete['return'] = returnFiber; childToDelete.effectTag = Deletion; // This might seem like it belongs on progressedFirstDeletion. However, // these children are not part of the reconciliation list of children. // Even if we abort and rereconcile the children, that will try to hydrate // again and the nodes are still in the host tree so these will be // recreated. if (returnFiber.lastEffect !== null) { returnFiber.lastEffect.nextEffect = childToDelete; returnFiber.lastEffect = childToDelete; } else { returnFiber.firstEffect = returnFiber.lastEffect = childToDelete; } } function insertNonHydratedInstance(returnFiber, fiber) { fiber.effectTag |= Placement; { switch (returnFiber.tag) { case HostRoot: { var parentContainer = returnFiber.stateNode.containerInfo; switch (fiber.tag) { case HostComponent: var type = fiber.type; var props = fiber.pendingProps; didNotFindHydratableContainerInstance(parentContainer, type, props); break; case HostText: var text = fiber.pendingProps; didNotFindHydratableContainerTextInstance(parentContainer, text); break; } break; } case HostComponent: { var parentType = returnFiber.type; var parentProps = returnFiber.memoizedProps; var parentInstance = returnFiber.stateNode; switch (fiber.tag) { case HostComponent: var _type = fiber.type; var _props = fiber.pendingProps; didNotFindHydratableInstance(parentType, parentProps, parentInstance, _type, _props); break; case HostText: var _text = fiber.pendingProps; didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, _text); break; } break; } default: return; } } } function tryHydrate(fiber, nextInstance) { switch (fiber.tag) { case HostComponent: { var type = fiber.type; var props = fiber.pendingProps; var instance = canHydrateInstance(nextInstance, type, props); if (instance !== null) { fiber.stateNode = instance; return true; } return false; } case HostText: { var text = fiber.pendingProps; var textInstance = canHydrateTextInstance(nextInstance, text); if (textInstance !== null) { fiber.stateNode = textInstance; return true; } return false; } default: return false; } } function tryToClaimNextHydratableInstance(fiber) { if (!isHydrating) { return; } var nextInstance = nextHydratableInstance; if (!nextInstance) { // Nothing to hydrate. Make it an insertion. insertNonHydratedInstance(hydrationParentFiber, fiber); isHydrating = false; hydrationParentFiber = fiber; return; } if (!tryHydrate(fiber, nextInstance)) { // If we can't hydrate this instance let's try the next one. // We use this as a heuristic. It's based on intuition and not data so it // might be flawed or unnecessary. nextInstance = getNextHydratableSibling(nextInstance); if (!nextInstance || !tryHydrate(fiber, nextInstance)) { // Nothing to hydrate. Make it an insertion. insertNonHydratedInstance(hydrationParentFiber, fiber); isHydrating = false; hydrationParentFiber = fiber; return; } // We matched the next one, we'll now assume that the first one was // superfluous and we'll delete it. Since we can't eagerly delete it // we'll have to schedule a deletion. To do that, this node needs a dummy // fiber associated with it. deleteHydratableInstance(hydrationParentFiber, nextHydratableInstance); } hydrationParentFiber = fiber; nextHydratableInstance = getFirstHydratableChild(nextInstance); } function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) { var instance = fiber.stateNode; var updatePayload = hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber); // TODO: Type this specific to this type of component. fiber.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there // is a new ref we mark this as an update. if (updatePayload !== null) { return true; } return false; } function prepareToHydrateHostTextInstance(fiber) { var textInstance = fiber.stateNode; var textContent = fiber.memoizedProps; var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber); { if (shouldUpdate) { // We assume that prepareToHydrateHostTextInstance is called in a context where the // hydration parent is the parent host component of this host text. var returnFiber = hydrationParentFiber; if (returnFiber !== null) { switch (returnFiber.tag) { case HostRoot: { var parentContainer = returnFiber.stateNode.containerInfo; didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, textContent); break; } case HostComponent: { var parentType = returnFiber.type; var parentProps = returnFiber.memoizedProps; var parentInstance = returnFiber.stateNode; didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, textContent); break; } } } } } return shouldUpdate; } function popToNextHostParent(fiber) { var parent = fiber['return']; while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot) { parent = parent['return']; } hydrationParentFiber = parent; } function popHydrationState(fiber) { if (fiber !== hydrationParentFiber) { // We're deeper than the current hydration context, inside an inserted // tree. return false; } if (!isHydrating) { // If we're not currently hydrating but we're in a hydration context, then // we were an insertion and now need to pop up reenter hydration of our // siblings. popToNextHostParent(fiber); isHydrating = true; return false; } var type = fiber.type; // If we have any remaining hydratable nodes, we need to delete them now. // We only do this deeper than head and body since they tend to have random // other nodes in them. We also ignore components with pure text content in // side of them. // TODO: Better heuristic. if (fiber.tag !== HostComponent || type !== 'head' && type !== 'body' && !shouldSetTextContent(type, fiber.memoizedProps)) { var nextInstance = nextHydratableInstance; while (nextInstance) { deleteHydratableInstance(fiber, nextInstance); nextInstance = getNextHydratableSibling(nextInstance); } } popToNextHostParent(fiber); nextHydratableInstance = hydrationParentFiber ? getNextHydratableSibling(fiber.stateNode) : null; return true; } function resetHydrationState() { hydrationParentFiber = null; nextHydratableInstance = null; isHydrating = false; } return { enterHydrationState: enterHydrationState, resetHydrationState: resetHydrationState, tryToClaimNextHydratableInstance: tryToClaimNextHydratableInstance, prepareToHydrateHostInstance: prepareToHydrateHostInstance, prepareToHydrateHostTextInstance: prepareToHydrateHostTextInstance, popHydrationState: popHydrationState }; }; // This lets us hook into Fiber to debug what it's doing. // See https://github.com/facebook/react/pull/8033. // This is not part of the public API, not even for React DevTools. // You may only inject a debugTool if you work on React Fiber itself. var ReactFiberInstrumentation = { debugTool: null }; var ReactFiberInstrumentation_1 = ReactFiberInstrumentation; var defaultShowDialog = function (capturedError) { return true; }; var showDialog = defaultShowDialog; function logCapturedError(capturedError) { var logError = showDialog(capturedError); // Allow injected showDialog() to prevent default console.error logging. // This enables renderers like ReactNative to better manage redbox behavior. if (logError === false) { return; } var error = capturedError.error; var suppressLogging = error && error.suppressReactErrorLogging; if (suppressLogging) { return; } { var componentName = capturedError.componentName, componentStack = capturedError.componentStack, errorBoundaryName = capturedError.errorBoundaryName, errorBoundaryFound = capturedError.errorBoundaryFound, willRetry = capturedError.willRetry; var componentNameMessage = componentName ? 'The above error occurred in the <' + componentName + '> component:' : 'The above error occurred in one of your React components:'; var errorBoundaryMessage = void 0; // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow. if (errorBoundaryFound && errorBoundaryName) { if (willRetry) { errorBoundaryMessage = 'React will try to recreate this component tree from scratch ' + ('using the error boundary you provided, ' + errorBoundaryName + '.'); } else { errorBoundaryMessage = 'This error was initially handled by the error boundary ' + errorBoundaryName + '.\n' + 'Recreating the tree from scratch failed so React will unmount the tree.'; } } else { errorBoundaryMessage = 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://fb.me/react-error-boundaries to learn more about error boundaries.'; } var combinedMessage = '' + componentNameMessage + componentStack + '\n\n' + ('' + errorBoundaryMessage); // In development, we provide our own message with just the component stack. // We don't include the original error message and JS stack because the browser // has already printed it. Even if the application swallows the error, it is still // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils. console.error(combinedMessage); } } var invokeGuardedCallback$1 = ReactErrorUtils.invokeGuardedCallback; var hasCaughtError = ReactErrorUtils.hasCaughtError; var clearCaughtError = ReactErrorUtils.clearCaughtError; { var didWarnAboutStateTransition = false; var didWarnSetStateChildContext = false; var didWarnStateUpdateForUnmountedComponent = {}; var warnAboutUpdateOnUnmounted = function (fiber) { var componentName = getComponentName(fiber) || 'ReactClass'; if (didWarnStateUpdateForUnmountedComponent[componentName]) { return; } warning(false, 'Can only update a mounted or mounting ' + 'component. This usually means you called setState, replaceState, ' + 'or forceUpdate on an unmounted component. This is a no-op.\n\nPlease ' + 'check the code for the %s component.', componentName); didWarnStateUpdateForUnmountedComponent[componentName] = true; }; var warnAboutInvalidUpdates = function (instance) { switch (ReactDebugCurrentFiber.phase) { case 'getChildContext': if (didWarnSetStateChildContext) { return; } warning(false, 'setState(...): Cannot call setState() inside getChildContext()'); didWarnSetStateChildContext = true; break; case 'render': if (didWarnAboutStateTransition) { return; } warning(false, 'Cannot update during an existing state transition (such as within ' + "`render` or another component's constructor). Render methods should " + 'be a pure function of props and state; constructor side-effects are ' + 'an anti-pattern, but can be moved to `componentWillMount`.'); didWarnAboutStateTransition = true; break; } }; } var ReactFiberScheduler = function (config) { var hostContext = ReactFiberHostContext(config); var hydrationContext = ReactFiberHydrationContext(config); var popHostContainer = hostContext.popHostContainer, popHostContext = hostContext.popHostContext, resetHostContainer = hostContext.resetHostContainer; var _ReactFiberBeginWork = ReactFiberBeginWork(config, hostContext, hydrationContext, scheduleWork, computeExpirationForFiber), beginWork = _ReactFiberBeginWork.beginWork, beginFailedWork = _ReactFiberBeginWork.beginFailedWork; var _ReactFiberCompleteWo = ReactFiberCompleteWork(config, hostContext, hydrationContext), completeWork = _ReactFiberCompleteWo.completeWork; var _ReactFiberCommitWork = ReactFiberCommitWork(config, captureError), commitResetTextContent = _ReactFiberCommitWork.commitResetTextContent, commitPlacement = _ReactFiberCommitWork.commitPlacement, commitDeletion = _ReactFiberCommitWork.commitDeletion, commitWork = _ReactFiberCommitWork.commitWork, commitLifeCycles = _ReactFiberCommitWork.commitLifeCycles, commitAttachRef = _ReactFiberCommitWork.commitAttachRef, commitDetachRef = _ReactFiberCommitWork.commitDetachRef; var now = config.now, scheduleDeferredCallback = config.scheduleDeferredCallback, cancelDeferredCallback = config.cancelDeferredCallback, useSyncScheduling = config.useSyncScheduling, prepareForCommit = config.prepareForCommit, resetAfterCommit = config.resetAfterCommit; // Represents the current time in ms. var startTime = now(); var mostRecentCurrentTime = msToExpirationTime(0); // Represents the expiration time that incoming updates should use. (If this // is NoWork, use the default strategy: async updates in async mode, sync // updates in sync mode.) var expirationContext = NoWork; var isWorking = false; // The next work in progress fiber that we're currently working on. var nextUnitOfWork = null; var nextRoot = null; // The time at which we're currently rendering work. var nextRenderExpirationTime = NoWork; // The next fiber with an effect that we're currently committing. var nextEffect = null; // Keep track of which fibers have captured an error that need to be handled. // Work is removed from this collection after componentDidCatch is called. var capturedErrors = null; // Keep track of which fibers have failed during the current batch of work. // This is a different set than capturedErrors, because it is not reset until // the end of the batch. This is needed to propagate errors correctly if a // subtree fails more than once. var failedBoundaries = null; // Error boundaries that captured an error during the current commit. var commitPhaseBoundaries = null; var firstUncaughtError = null; var didFatal = false; var isCommitting = false; var isUnmounting = false; // Used for performance tracking. var interruptedBy = null; function resetContextStack() { // Reset the stack reset$1(); // Reset the cursors resetContext(); resetHostContainer(); } function commitAllHostEffects() { while (nextEffect !== null) { { ReactDebugCurrentFiber.setCurrentFiber(nextEffect); } recordEffect(); var effectTag = nextEffect.effectTag; if (effectTag & ContentReset) { commitResetTextContent(nextEffect); } if (effectTag & Ref) { var current = nextEffect.alternate; if (current !== null) { commitDetachRef(current); } } // The following switch statement is only concerned about placement, // updates, and deletions. To avoid needing to add a case for every // possible bitmap value, we remove the secondary effects from the // effect tag and switch on that value. var primaryEffectTag = effectTag & ~(Callback | Err | ContentReset | Ref | PerformedWork); switch (primaryEffectTag) { case Placement: { commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is inserted, before // any life-cycles like componentDidMount gets called. // TODO: findDOMNode doesn't rely on this any more but isMounted // does and isMounted is deprecated anyway so we should be able // to kill this. nextEffect.effectTag &= ~Placement; break; } case PlacementAndUpdate: { // Placement commitPlacement(nextEffect); // Clear the "placement" from effect tag so that we know that this is inserted, before // any life-cycles like componentDidMount gets called. nextEffect.effectTag &= ~Placement; // Update var _current = nextEffect.alternate; commitWork(_current, nextEffect); break; } case Update: { var _current2 = nextEffect.alternate; commitWork(_current2, nextEffect); break; } case Deletion: { isUnmounting = true; commitDeletion(nextEffect); isUnmounting = false; break; } } nextEffect = nextEffect.nextEffect; } { ReactDebugCurrentFiber.resetCurrentFiber(); } } function commitAllLifeCycles() { while (nextEffect !== null) { var effectTag = nextEffect.effectTag; if (effectTag & (Update | Callback)) { recordEffect(); var current = nextEffect.alternate; commitLifeCycles(current, nextEffect); } if (effectTag & Ref) { recordEffect(); commitAttachRef(nextEffect); } if (effectTag & Err) { recordEffect(); commitErrorHandling(nextEffect); } var next = nextEffect.nextEffect; // Ensure that we clean these up so that we don't accidentally keep them. // I'm not actually sure this matters because we can't reset firstEffect // and lastEffect since they're on every node, not just the effectful // ones. So we have to clean everything as we reuse nodes anyway. nextEffect.nextEffect = null; // Ensure that we reset the effectTag here so that we can rely on effect // tags to reason about the current life-cycle. nextEffect = next; } } function commitRoot(finishedWork) { // We keep track of this so that captureError can collect any boundaries // that capture an error during the commit phase. The reason these aren't // local to this function is because errors that occur during cWU are // captured elsewhere, to prevent the unmount from being interrupted. isWorking = true; isCommitting = true; startCommitTimer(); var root = finishedWork.stateNode; !(root.current !== finishedWork) ? invariant(false, 'Cannot commit the same tree as before. This is probably a bug related to the return field. This error is likely caused by a bug in React. Please file an issue.') : void 0; root.isReadyForCommit = false; // Reset this to null before calling lifecycles ReactCurrentOwner.current = null; var firstEffect = void 0; if (finishedWork.effectTag > PerformedWork) { // A fiber's effect list consists only of its children, not itself. So if // the root has an effect, we need to add it to the end of the list. The // resulting list is the set that would belong to the root's parent, if // it had one; that is, all the effects in the tree including the root. if (finishedWork.lastEffect !== null) { finishedWork.lastEffect.nextEffect = finishedWork; firstEffect = finishedWork.firstEffect; } else { firstEffect = finishedWork; } } else { // There is no effect on the root. firstEffect = finishedWork.firstEffect; } prepareForCommit(); // Commit all the side-effects within a tree. We'll do this in two passes. // The first pass performs all the host insertions, updates, deletions and // ref unmounts. nextEffect = firstEffect; startCommitHostEffectsTimer(); while (nextEffect !== null) { var didError = false; var _error = void 0; { invokeGuardedCallback$1(null, commitAllHostEffects, null); if (hasCaughtError()) { didError = true; _error = clearCaughtError(); } } if (didError) { !(nextEffect !== null) ? invariant(false, 'Should have next effect. This error is likely caused by a bug in React. Please file an issue.') : void 0; captureError(nextEffect, _error); // Clean-up if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } } } stopCommitHostEffectsTimer(); resetAfterCommit(); // The work-in-progress tree is now the current tree. This must come after // the first pass of the commit phase, so that the previous tree is still // current during componentWillUnmount, but before the second pass, so that // the finished work is current during componentDidMount/Update. root.current = finishedWork; // In the second pass we'll perform all life-cycles and ref callbacks. // Life-cycles happen as a separate pass so that all placements, updates, // and deletions in the entire tree have already been invoked. // This pass also triggers any renderer-specific initial effects. nextEffect = firstEffect; startCommitLifeCyclesTimer(); while (nextEffect !== null) { var _didError = false; var _error2 = void 0; { invokeGuardedCallback$1(null, commitAllLifeCycles, null); if (hasCaughtError()) { _didError = true; _error2 = clearCaughtError(); } } if (_didError) { !(nextEffect !== null) ? invariant(false, 'Should have next effect. This error is likely caused by a bug in React. Please file an issue.') : void 0; captureError(nextEffect, _error2); if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } } } isCommitting = false; isWorking = false; stopCommitLifeCyclesTimer(); stopCommitTimer(); if (typeof onCommitRoot === 'function') { onCommitRoot(finishedWork.stateNode); } if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onCommitWork(finishedWork); } // If we caught any errors during this commit, schedule their boundaries // to update. if (commitPhaseBoundaries) { commitPhaseBoundaries.forEach(scheduleErrorRecovery); commitPhaseBoundaries = null; } if (firstUncaughtError !== null) { var _error3 = firstUncaughtError; firstUncaughtError = null; onUncaughtError(_error3); } var remainingTime = root.current.expirationTime; if (remainingTime === NoWork) { capturedErrors = null; failedBoundaries = null; } return remainingTime; } function resetExpirationTime(workInProgress, renderTime) { if (renderTime !== Never && workInProgress.expirationTime === Never) { // The children of this component are hidden. Don't bubble their // expiration times. return; } // Check for pending updates. var newExpirationTime = getUpdateExpirationTime(workInProgress); // TODO: Calls need to visit stateNode // Bubble up the earliest expiration time. var child = workInProgress.child; while (child !== null) { if (child.expirationTime !== NoWork && (newExpirationTime === NoWork || newExpirationTime > child.expirationTime)) { newExpirationTime = child.expirationTime; } child = child.sibling; } workInProgress.expirationTime = newExpirationTime; } function completeUnitOfWork(workInProgress) { while (true) { // The current, flushed, state of this fiber is the alternate. // Ideally nothing should rely on this, but relying on it here // means that we don't need an additional field on the work in // progress. var current = workInProgress.alternate; { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } var next = completeWork(current, workInProgress, nextRenderExpirationTime); { ReactDebugCurrentFiber.resetCurrentFiber(); } var returnFiber = workInProgress['return']; var siblingFiber = workInProgress.sibling; resetExpirationTime(workInProgress, nextRenderExpirationTime); if (next !== null) { stopWorkTimer(workInProgress); if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); } // If completing this work spawned new work, do that next. We'll come // back here again. return next; } if (returnFiber !== null) { // Append all the effects of the subtree and this fiber onto the effect // list of the parent. The completion order of the children affects the // side-effect order. if (returnFiber.firstEffect === null) { returnFiber.firstEffect = workInProgress.firstEffect; } if (workInProgress.lastEffect !== null) { if (returnFiber.lastEffect !== null) { returnFiber.lastEffect.nextEffect = workInProgress.firstEffect; } returnFiber.lastEffect = workInProgress.lastEffect; } // If this fiber had side-effects, we append it AFTER the children's // side-effects. We can perform certain side-effects earlier if // needed, by doing multiple passes over the effect list. We don't want // to schedule our own side-effect on our own list because if end up // reusing children we'll schedule this effect onto itself since we're // at the end. var effectTag = workInProgress.effectTag; // Skip both NoWork and PerformedWork tags when creating the effect list. // PerformedWork effect is read by React DevTools but shouldn't be committed. if (effectTag > PerformedWork) { if (returnFiber.lastEffect !== null) { returnFiber.lastEffect.nextEffect = workInProgress; } else { returnFiber.firstEffect = workInProgress; } returnFiber.lastEffect = workInProgress; } } stopWorkTimer(workInProgress); if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); } if (siblingFiber !== null) { // If there is more work to do in this returnFiber, do that next. return siblingFiber; } else if (returnFiber !== null) { // If there's no more work in this returnFiber. Complete the returnFiber. workInProgress = returnFiber; continue; } else { // We've reached the root. var root = workInProgress.stateNode; root.isReadyForCommit = true; return null; } } // Without this explicit null return Flow complains of invalid return type // TODO Remove the above while(true) loop // eslint-disable-next-line no-unreachable return null; } function performUnitOfWork(workInProgress) { // The current, flushed, state of this fiber is the alternate. // Ideally nothing should rely on this, but relying on it here // means that we don't need an additional field on the work in // progress. var current = workInProgress.alternate; // See if beginning this work spawns more work. startWorkTimer(workInProgress); { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } var next = beginWork(current, workInProgress, nextRenderExpirationTime); { ReactDebugCurrentFiber.resetCurrentFiber(); } if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); } if (next === null) { // If this doesn't spawn new work, complete the current work. next = completeUnitOfWork(workInProgress); } ReactCurrentOwner.current = null; return next; } function performFailedUnitOfWork(workInProgress) { // The current, flushed, state of this fiber is the alternate. // Ideally nothing should rely on this, but relying on it here // means that we don't need an additional field on the work in // progress. var current = workInProgress.alternate; // See if beginning this work spawns more work. startWorkTimer(workInProgress); { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } var next = beginFailedWork(current, workInProgress, nextRenderExpirationTime); { ReactDebugCurrentFiber.resetCurrentFiber(); } if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); } if (next === null) { // If this doesn't spawn new work, complete the current work. next = completeUnitOfWork(workInProgress); } ReactCurrentOwner.current = null; return next; } function workLoop(expirationTime) { if (capturedErrors !== null) { // If there are unhandled errors, switch to the slow work loop. // TODO: How to avoid this check in the fast path? Maybe the renderer // could keep track of which roots have unhandled errors and call a // forked version of renderRoot. slowWorkLoopThatChecksForFailedWork(expirationTime); return; } if (nextRenderExpirationTime === NoWork || nextRenderExpirationTime > expirationTime) { return; } if (nextRenderExpirationTime <= mostRecentCurrentTime) { // Flush all expired work. while (nextUnitOfWork !== null) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } else { // Flush asynchronous work until the deadline runs out of time. while (nextUnitOfWork !== null && !shouldYield()) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } } function slowWorkLoopThatChecksForFailedWork(expirationTime) { if (nextRenderExpirationTime === NoWork || nextRenderExpirationTime > expirationTime) { return; } if (nextRenderExpirationTime <= mostRecentCurrentTime) { // Flush all expired work. while (nextUnitOfWork !== null) { if (hasCapturedError(nextUnitOfWork)) { // Use a forked version of performUnitOfWork nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork); } else { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } } else { // Flush asynchronous work until the deadline runs out of time. while (nextUnitOfWork !== null && !shouldYield()) { if (hasCapturedError(nextUnitOfWork)) { // Use a forked version of performUnitOfWork nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork); } else { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } } } function renderRootCatchBlock(root, failedWork, boundary, expirationTime) { // We're going to restart the error boundary that captured the error. // Conceptually, we're unwinding the stack. We need to unwind the // context stack, too. unwindContexts(failedWork, boundary); // Restart the error boundary using a forked version of // performUnitOfWork that deletes the boundary's children. The entire // failed subree will be unmounted. During the commit phase, a special // lifecycle method is called on the error boundary, which triggers // a re-render. nextUnitOfWork = performFailedUnitOfWork(boundary); // Continue working. workLoop(expirationTime); } function renderRoot(root, expirationTime) { !!isWorking ? invariant(false, 'renderRoot was called recursively. This error is likely caused by a bug in React. Please file an issue.') : void 0; isWorking = true; // We're about to mutate the work-in-progress tree. If the root was pending // commit, it no longer is: we'll need to complete it again. root.isReadyForCommit = false; // Check if we're starting from a fresh stack, or if we're resuming from // previously yielded work. if (root !== nextRoot || expirationTime !== nextRenderExpirationTime || nextUnitOfWork === null) { // Reset the stack and start working from the root. resetContextStack(); nextRoot = root; nextRenderExpirationTime = expirationTime; nextUnitOfWork = createWorkInProgress(nextRoot.current, null, expirationTime); } startWorkLoopTimer(nextUnitOfWork); var didError = false; var error = null; { invokeGuardedCallback$1(null, workLoop, null, expirationTime); if (hasCaughtError()) { didError = true; error = clearCaughtError(); } } // An error was thrown during the render phase. while (didError) { if (didFatal) { // This was a fatal error. Don't attempt to recover from it. firstUncaughtError = error; break; } var failedWork = nextUnitOfWork; if (failedWork === null) { // An error was thrown but there's no current unit of work. This can // happen during the commit phase if there's a bug in the renderer. didFatal = true; continue; } // "Capture" the error by finding the nearest boundary. If there is no // error boundary, we use the root. var boundary = captureError(failedWork, error); !(boundary !== null) ? invariant(false, 'Should have found an error boundary. This error is likely caused by a bug in React. Please file an issue.') : void 0; if (didFatal) { // The error we just captured was a fatal error. This happens // when the error propagates to the root more than once. continue; } didError = false; error = null; { invokeGuardedCallback$1(null, renderRootCatchBlock, null, root, failedWork, boundary, expirationTime); if (hasCaughtError()) { didError = true; error = clearCaughtError(); continue; } } // We're finished working. Exit the error loop. break; } var uncaughtError = firstUncaughtError; // We're done performing work. Time to clean up. stopWorkLoopTimer(interruptedBy); interruptedBy = null; isWorking = false; didFatal = false; firstUncaughtError = null; if (uncaughtError !== null) { onUncaughtError(uncaughtError); } return root.isReadyForCommit ? root.current.alternate : null; } // Returns the boundary that captured the error, or null if the error is ignored function captureError(failedWork, error) { // It is no longer valid because we exited the user code. ReactCurrentOwner.current = null; { ReactDebugCurrentFiber.resetCurrentFiber(); } // Search for the nearest error boundary. var boundary = null; // Passed to logCapturedError() var errorBoundaryFound = false; var willRetry = false; var errorBoundaryName = null; // Host containers are a special case. If the failed work itself is a host // container, then it acts as its own boundary. In all other cases, we // ignore the work itself and only search through the parents. if (failedWork.tag === HostRoot) { boundary = failedWork; if (isFailedBoundary(failedWork)) { // If this root already failed, there must have been an error when // attempting to unmount it. This is a worst-case scenario and // should only be possible if there's a bug in the renderer. didFatal = true; } } else { var node = failedWork['return']; while (node !== null && boundary === null) { if (node.tag === ClassComponent) { var instance = node.stateNode; if (typeof instance.componentDidCatch === 'function') { errorBoundaryFound = true; errorBoundaryName = getComponentName(node); // Found an error boundary! boundary = node; willRetry = true; } } else if (node.tag === HostRoot) { // Treat the root like a no-op error boundary boundary = node; } if (isFailedBoundary(node)) { // This boundary is already in a failed state. // If we're currently unmounting, that means this error was // thrown while unmounting a failed subtree. We should ignore // the error. if (isUnmounting) { return null; } // If we're in the commit phase, we should check to see if // this boundary already captured an error during this commit. // This case exists because multiple errors can be thrown during // a single commit without interruption. if (commitPhaseBoundaries !== null && (commitPhaseBoundaries.has(node) || node.alternate !== null && commitPhaseBoundaries.has(node.alternate))) { // If so, we should ignore this error. return null; } // The error should propagate to the next boundary -— we keep looking. boundary = null; willRetry = false; } node = node['return']; } } if (boundary !== null) { // Add to the collection of failed boundaries. This lets us know that // subsequent errors in this subtree should propagate to the next boundary. if (failedBoundaries === null) { failedBoundaries = new Set(); } failedBoundaries.add(boundary); // This method is unsafe outside of the begin and complete phases. // We might be in the commit phase when an error is captured. // The risk is that the return path from this Fiber may not be accurate. // That risk is acceptable given the benefit of providing users more context. var _componentStack = getStackAddendumByWorkInProgressFiber(failedWork); var _componentName = getComponentName(failedWork); // Add to the collection of captured errors. This is stored as a global // map of errors and their component stack location keyed by the boundaries // that capture them. We mostly use this Map as a Set; it's a Map only to // avoid adding a field to Fiber to store the error. if (capturedErrors === null) { capturedErrors = new Map(); } var capturedError = { componentName: _componentName, componentStack: _componentStack, error: error, errorBoundary: errorBoundaryFound ? boundary.stateNode : null, errorBoundaryFound: errorBoundaryFound, errorBoundaryName: errorBoundaryName, willRetry: willRetry }; capturedErrors.set(boundary, capturedError); try { logCapturedError(capturedError); } catch (e) { // Prevent cycle if logCapturedError() throws. // A cycle may still occur if logCapturedError renders a component that throws. var suppressLogging = e && e.suppressReactErrorLogging; if (!suppressLogging) { console.error(e); } } // If we're in the commit phase, defer scheduling an update on the // boundary until after the commit is complete if (isCommitting) { if (commitPhaseBoundaries === null) { commitPhaseBoundaries = new Set(); } commitPhaseBoundaries.add(boundary); } else { // Otherwise, schedule an update now. // TODO: Is this actually necessary during the render phase? Is it // possible to unwind and continue rendering at the same priority, // without corrupting internal state? scheduleErrorRecovery(boundary); } return boundary; } else if (firstUncaughtError === null) { // If no boundary is found, we'll need to throw the error firstUncaughtError = error; } return null; } function hasCapturedError(fiber) { // TODO: capturedErrors should store the boundary instance, to avoid needing // to check the alternate. return capturedErrors !== null && (capturedErrors.has(fiber) || fiber.alternate !== null && capturedErrors.has(fiber.alternate)); } function isFailedBoundary(fiber) { // TODO: failedBoundaries should store the boundary instance, to avoid // needing to check the alternate. return failedBoundaries !== null && (failedBoundaries.has(fiber) || fiber.alternate !== null && failedBoundaries.has(fiber.alternate)); } function commitErrorHandling(effectfulFiber) { var capturedError = void 0; if (capturedErrors !== null) { capturedError = capturedErrors.get(effectfulFiber); capturedErrors['delete'](effectfulFiber); if (capturedError == null) { if (effectfulFiber.alternate !== null) { effectfulFiber = effectfulFiber.alternate; capturedError = capturedErrors.get(effectfulFiber); capturedErrors['delete'](effectfulFiber); } } } !(capturedError != null) ? invariant(false, 'No error for given unit of work. This error is likely caused by a bug in React. Please file an issue.') : void 0; switch (effectfulFiber.tag) { case ClassComponent: var instance = effectfulFiber.stateNode; var info = { componentStack: capturedError.componentStack }; // Allow the boundary to handle the error, usually by scheduling // an update to itself instance.componentDidCatch(capturedError.error, info); return; case HostRoot: if (firstUncaughtError === null) { firstUncaughtError = capturedError.error; } return; default: invariant(false, 'Invalid type of work. This error is likely caused by a bug in React. Please file an issue.'); } } function unwindContexts(from, to) { var node = from; while (node !== null) { switch (node.tag) { case ClassComponent: popContextProvider(node); break; case HostComponent: popHostContext(node); break; case HostRoot: popHostContainer(node); break; case HostPortal: popHostContainer(node); break; } if (node === to || node.alternate === to) { stopFailedWorkTimer(node); break; } else { stopWorkTimer(node); } node = node['return']; } } function computeAsyncExpiration() { // Given the current clock time, returns an expiration time. We use rounding // to batch like updates together. // Should complete within ~1000ms. 1200ms max. var currentTime = recalculateCurrentTime(); var expirationMs = 1000; var bucketSizeMs = 200; return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs); } function computeExpirationForFiber(fiber) { var expirationTime = void 0; if (expirationContext !== NoWork) { // An explicit expiration context was set; expirationTime = expirationContext; } else if (isWorking) { if (isCommitting) { // Updates that occur during the commit phase should have sync priority // by default. expirationTime = Sync; } else { // Updates during the render phase should expire at the same time as // the work that is being rendered. expirationTime = nextRenderExpirationTime; } } else { // No explicit expiration context was set, and we're not currently // performing work. Calculate a new expiration time. if (useSyncScheduling && !(fiber.internalContextTag & AsyncUpdates)) { // This is a sync update expirationTime = Sync; } else { // This is an async update expirationTime = computeAsyncExpiration(); } } return expirationTime; } function scheduleWork(fiber, expirationTime) { return scheduleWorkImpl(fiber, expirationTime, false); } function checkRootNeedsClearing(root, fiber, expirationTime) { if (!isWorking && root === nextRoot && expirationTime < nextRenderExpirationTime) { // Restart the root from the top. if (nextUnitOfWork !== null) { // This is an interruption. (Used for performance tracking.) interruptedBy = fiber; } nextRoot = null; nextUnitOfWork = null; nextRenderExpirationTime = NoWork; } } function scheduleWorkImpl(fiber, expirationTime, isErrorRecovery) { recordScheduleUpdate(); { if (!isErrorRecovery && fiber.tag === ClassComponent) { var instance = fiber.stateNode; warnAboutInvalidUpdates(instance); } } var node = fiber; while (node !== null) { // Walk the parent path to the root and update each node's // expiration time. if (node.expirationTime === NoWork || node.expirationTime > expirationTime) { node.expirationTime = expirationTime; } if (node.alternate !== null) { if (node.alternate.expirationTime === NoWork || node.alternate.expirationTime > expirationTime) { node.alternate.expirationTime = expirationTime; } } if (node['return'] === null) { if (node.tag === HostRoot) { var root = node.stateNode; checkRootNeedsClearing(root, fiber, expirationTime); requestWork(root, expirationTime); checkRootNeedsClearing(root, fiber, expirationTime); } else { { if (!isErrorRecovery && fiber.tag === ClassComponent) { warnAboutUpdateOnUnmounted(fiber); } } return; } } node = node['return']; } } function scheduleErrorRecovery(fiber) { scheduleWorkImpl(fiber, Sync, true); } function recalculateCurrentTime() { // Subtract initial time so it fits inside 32bits var ms = now() - startTime; mostRecentCurrentTime = msToExpirationTime(ms); return mostRecentCurrentTime; } function deferredUpdates(fn) { var previousExpirationContext = expirationContext; expirationContext = computeAsyncExpiration(); try { return fn(); } finally { expirationContext = previousExpirationContext; } } function syncUpdates(fn) { var previousExpirationContext = expirationContext; expirationContext = Sync; try { return fn(); } finally { expirationContext = previousExpirationContext; } } // TODO: Everything below this is written as if it has been lifted to the // renderers. I'll do this in a follow-up. // Linked-list of roots var firstScheduledRoot = null; var lastScheduledRoot = null; var callbackExpirationTime = NoWork; var callbackID = -1; var isRendering = false; var nextFlushedRoot = null; var nextFlushedExpirationTime = NoWork; var deadlineDidExpire = false; var hasUnhandledError = false; var unhandledError = null; var deadline = null; var isBatchingUpdates = false; var isUnbatchingUpdates = false; // Use these to prevent an infinite loop of nested updates var NESTED_UPDATE_LIMIT = 1000; var nestedUpdateCount = 0; var timeHeuristicForUnitOfWork = 1; function scheduleCallbackWithExpiration(expirationTime) { if (callbackExpirationTime !== NoWork) { // A callback is already scheduled. Check its expiration time (timeout). if (expirationTime > callbackExpirationTime) { // Existing callback has sufficient timeout. Exit. return; } else { // Existing callback has insufficient timeout. Cancel and schedule a // new one. cancelDeferredCallback(callbackID); } // The request callback timer is already running. Don't start a new one. } else { startRequestCallbackTimer(); } // Compute a timeout for the given expiration time. var currentMs = now() - startTime; var expirationMs = expirationTimeToMs(expirationTime); var timeout = expirationMs - currentMs; callbackExpirationTime = expirationTime; callbackID = scheduleDeferredCallback(performAsyncWork, { timeout: timeout }); } // requestWork is called by the scheduler whenever a root receives an update. // It's up to the renderer to call renderRoot at some point in the future. function requestWork(root, expirationTime) { if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { invariant(false, 'Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.'); } // Add the root to the schedule. // Check if this root is already part of the schedule. if (root.nextScheduledRoot === null) { // This root is not already scheduled. Add it. root.remainingExpirationTime = expirationTime; if (lastScheduledRoot === null) { firstScheduledRoot = lastScheduledRoot = root; root.nextScheduledRoot = root; } else { lastScheduledRoot.nextScheduledRoot = root; lastScheduledRoot = root; lastScheduledRoot.nextScheduledRoot = firstScheduledRoot; } } else { // This root is already scheduled, but its priority may have increased. var remainingExpirationTime = root.remainingExpirationTime; if (remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime) { // Update the priority. root.remainingExpirationTime = expirationTime; } } if (isRendering) { // Prevent reentrancy. Remaining work will be scheduled at the end of // the currently rendering batch. return; } if (isBatchingUpdates) { // Flush work at the end of the batch. if (isUnbatchingUpdates) { // ...unless we're inside unbatchedUpdates, in which case we should // flush it now. nextFlushedRoot = root; nextFlushedExpirationTime = Sync; performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime); } return; } // TODO: Get rid of Sync and use current time? if (expirationTime === Sync) { performWork(Sync, null); } else { scheduleCallbackWithExpiration(expirationTime); } } function findHighestPriorityRoot() { var highestPriorityWork = NoWork; var highestPriorityRoot = null; if (lastScheduledRoot !== null) { var previousScheduledRoot = lastScheduledRoot; var root = firstScheduledRoot; while (root !== null) { var remainingExpirationTime = root.remainingExpirationTime; if (remainingExpirationTime === NoWork) { // This root no longer has work. Remove it from the scheduler. // TODO: This check is redudant, but Flow is confused by the branch // below where we set lastScheduledRoot to null, even though we break // from the loop right after. !(previousScheduledRoot !== null && lastScheduledRoot !== null) ? invariant(false, 'Should have a previous and last root. This error is likely caused by a bug in React. Please file an issue.') : void 0; if (root === root.nextScheduledRoot) { // This is the only root in the list. root.nextScheduledRoot = null; firstScheduledRoot = lastScheduledRoot = null; break; } else if (root === firstScheduledRoot) { // This is the first root in the list. var next = root.nextScheduledRoot; firstScheduledRoot = next; lastScheduledRoot.nextScheduledRoot = next; root.nextScheduledRoot = null; } else if (root === lastScheduledRoot) { // This is the last root in the list. lastScheduledRoot = previousScheduledRoot; lastScheduledRoot.nextScheduledRoot = firstScheduledRoot; root.nextScheduledRoot = null; break; } else { previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot; root.nextScheduledRoot = null; } root = previousScheduledRoot.nextScheduledRoot; } else { if (highestPriorityWork === NoWork || remainingExpirationTime < highestPriorityWork) { // Update the priority, if it's higher highestPriorityWork = remainingExpirationTime; highestPriorityRoot = root; } if (root === lastScheduledRoot) { break; } previousScheduledRoot = root; root = root.nextScheduledRoot; } } } // If the next root is the same as the previous root, this is a nested // update. To prevent an infinite loop, increment the nested update count. var previousFlushedRoot = nextFlushedRoot; if (previousFlushedRoot !== null && previousFlushedRoot === highestPriorityRoot) { nestedUpdateCount++; } else { // Reset whenever we switch roots. nestedUpdateCount = 0; } nextFlushedRoot = highestPriorityRoot; nextFlushedExpirationTime = highestPriorityWork; } function performAsyncWork(dl) { performWork(NoWork, dl); } function performWork(minExpirationTime, dl) { deadline = dl; // Keep working on roots until there's no more work, or until the we reach // the deadline. findHighestPriorityRoot(); if (enableUserTimingAPI && deadline !== null) { var didExpire = nextFlushedExpirationTime < recalculateCurrentTime(); stopRequestCallbackTimer(didExpire); } while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || nextFlushedExpirationTime <= minExpirationTime) && !deadlineDidExpire) { performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime); // Find the next highest priority work. findHighestPriorityRoot(); } // We're done flushing work. Either we ran out of time in this callback, // or there's no more work left with sufficient priority. // If we're inside a callback, set this to false since we just completed it. if (deadline !== null) { callbackExpirationTime = NoWork; callbackID = -1; } // If there's work left over, schedule a new callback. if (nextFlushedExpirationTime !== NoWork) { scheduleCallbackWithExpiration(nextFlushedExpirationTime); } // Clean-up. deadline = null; deadlineDidExpire = false; nestedUpdateCount = 0; if (hasUnhandledError) { var _error4 = unhandledError; unhandledError = null; hasUnhandledError = false; throw _error4; } } function performWorkOnRoot(root, expirationTime) { !!isRendering ? invariant(false, 'performWorkOnRoot was called recursively. This error is likely caused by a bug in React. Please file an issue.') : void 0; isRendering = true; // Check if this is async work or sync/expired work. // TODO: Pass current time as argument to renderRoot, commitRoot if (expirationTime <= recalculateCurrentTime()) { // Flush sync work. var finishedWork = root.finishedWork; if (finishedWork !== null) { // This root is already complete. We can commit it. root.finishedWork = null; root.remainingExpirationTime = commitRoot(finishedWork); } else { root.finishedWork = null; finishedWork = renderRoot(root, expirationTime); if (finishedWork !== null) { // We've completed the root. Commit it. root.remainingExpirationTime = commitRoot(finishedWork); } } } else { // Flush async work. var _finishedWork = root.finishedWork; if (_finishedWork !== null) { // This root is already complete. We can commit it. root.finishedWork = null; root.remainingExpirationTime = commitRoot(_finishedWork); } else { root.finishedWork = null; _finishedWork = renderRoot(root, expirationTime); if (_finishedWork !== null) { // We've completed the root. Check the deadline one more time // before committing. if (!shouldYield()) { // Still time left. Commit the root. root.remainingExpirationTime = commitRoot(_finishedWork); } else { // There's no time left. Mark this root as complete. We'll come // back and commit it later. root.finishedWork = _finishedWork; } } } } isRendering = false; } // When working on async work, the reconciler asks the renderer if it should // yield execution. For DOM, we implement this with requestIdleCallback. function shouldYield() { if (deadline === null) { return false; } if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) { // Disregard deadline.didTimeout. Only expired work should be flushed // during a timeout. This path is only hit for non-expired work. return false; } deadlineDidExpire = true; return true; } // TODO: Not happy about this hook. Conceptually, renderRoot should return a // tuple of (isReadyForCommit, didError, error) function onUncaughtError(error) { !(nextFlushedRoot !== null) ? invariant(false, 'Should be working on a root. This error is likely caused by a bug in React. Please file an issue.') : void 0; // Unschedule this root so we don't work on it again until there's // another update. nextFlushedRoot.remainingExpirationTime = NoWork; if (!hasUnhandledError) { hasUnhandledError = true; unhandledError = error; } } // TODO: Batching should be implemented at the renderer level, not inside // the reconciler. function batchedUpdates(fn, a) { var previousIsBatchingUpdates = isBatchingUpdates; isBatchingUpdates = true; try { return fn(a); } finally { isBatchingUpdates = previousIsBatchingUpdates; if (!isBatchingUpdates && !isRendering) { performWork(Sync, null); } } } // TODO: Batching should be implemented at the renderer level, not inside // the reconciler. function unbatchedUpdates(fn) { if (isBatchingUpdates && !isUnbatchingUpdates) { isUnbatchingUpdates = true; try { return fn(); } finally { isUnbatchingUpdates = false; } } return fn(); } // TODO: Batching should be implemented at the renderer level, not within // the reconciler. function flushSync(fn) { var previousIsBatchingUpdates = isBatchingUpdates; isBatchingUpdates = true; try { return syncUpdates(fn); } finally { isBatchingUpdates = previousIsBatchingUpdates; !!isRendering ? invariant(false, 'flushSync was called from inside a lifecycle method. It cannot be called when React is already rendering.') : void 0; performWork(Sync, null); } } return { computeAsyncExpiration: computeAsyncExpiration, computeExpirationForFiber: computeExpirationForFiber, scheduleWork: scheduleWork, batchedUpdates: batchedUpdates, unbatchedUpdates: unbatchedUpdates, flushSync: flushSync, deferredUpdates: deferredUpdates }; }; { var didWarnAboutNestedUpdates = false; } // 0 is PROD, 1 is DEV. // Might add PROFILE later. function getContextForSubtree(parentComponent) { if (!parentComponent) { return emptyObject; } var fiber = get(parentComponent); var parentContext = findCurrentUnmaskedContext(fiber); return isContextProvider(fiber) ? processChildContext(fiber, parentContext) : parentContext; } var ReactFiberReconciler$1 = function (config) { var getPublicInstance = config.getPublicInstance; var _ReactFiberScheduler = ReactFiberScheduler(config), computeAsyncExpiration = _ReactFiberScheduler.computeAsyncExpiration, computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber, scheduleWork = _ReactFiberScheduler.scheduleWork, batchedUpdates = _ReactFiberScheduler.batchedUpdates, unbatchedUpdates = _ReactFiberScheduler.unbatchedUpdates, flushSync = _ReactFiberScheduler.flushSync, deferredUpdates = _ReactFiberScheduler.deferredUpdates; function scheduleTopLevelUpdate(current, element, callback) { { if (ReactDebugCurrentFiber.phase === 'render' && ReactDebugCurrentFiber.current !== null && !didWarnAboutNestedUpdates) { didWarnAboutNestedUpdates = true; warning(false, '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.\n\n' + 'Check the render method of %s.', getComponentName(ReactDebugCurrentFiber.current) || 'Unknown'); } } callback = callback === undefined ? null : callback; { warning(callback === null || typeof callback === 'function', 'render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback); } var expirationTime = void 0; // Check if the top-level element is an async wrapper component. If so, // treat updates to the root as async. This is a bit weird but lets us // avoid a separate `renderAsync` API. if (enableAsyncSubtreeAPI && element != null && element.type != null && element.type.prototype != null && element.type.prototype.unstable_isAsyncReactComponent === true) { expirationTime = computeAsyncExpiration(); } else { expirationTime = computeExpirationForFiber(current); } var update = { expirationTime: expirationTime, partialState: { element: element }, callback: callback, isReplace: false, isForced: false, nextCallback: null, next: null }; insertUpdateIntoFiber(current, update); scheduleWork(current, expirationTime); } function findHostInstance(fiber) { var hostFiber = findCurrentHostFiber(fiber); if (hostFiber === null) { return null; } return hostFiber.stateNode; } return { createContainer: function (containerInfo, hydrate) { return createFiberRoot(containerInfo, hydrate); }, updateContainer: function (element, container, parentComponent, callback) { // TODO: If this is a nested container, this won't be the root. var current = container.current; { if (ReactFiberInstrumentation_1.debugTool) { if (current.alternate === null) { ReactFiberInstrumentation_1.debugTool.onMountContainer(container); } else if (element === null) { ReactFiberInstrumentation_1.debugTool.onUnmountContainer(container); } else { ReactFiberInstrumentation_1.debugTool.onUpdateContainer(container); } } } var context = getContextForSubtree(parentComponent); if (container.context === null) { container.context = context; } else { container.pendingContext = context; } scheduleTopLevelUpdate(current, element, callback); }, batchedUpdates: batchedUpdates, unbatchedUpdates: unbatchedUpdates, deferredUpdates: deferredUpdates, flushSync: flushSync, getPublicRootInstance: function (container) { var containerFiber = container.current; if (!containerFiber.child) { return null; } switch (containerFiber.child.tag) { case HostComponent: return getPublicInstance(containerFiber.child.stateNode); default: return containerFiber.child.stateNode; } }, findHostInstance: findHostInstance, findHostInstanceWithNoPortals: function (fiber) { var hostFiber = findCurrentHostFiberWithNoPortals(fiber); if (hostFiber === null) { return null; } return hostFiber.stateNode; }, injectIntoDevTools: function (devToolsConfig) { var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance; return injectInternals(_assign({}, devToolsConfig, { findHostInstanceByFiber: function (fiber) { return findHostInstance(fiber); }, findFiberByHostInstance: function (instance) { if (!findFiberByHostInstance) { // Might not be implemented by the renderer. return null; } return findFiberByHostInstance(instance); } })); } }; }; var ReactFiberReconciler$2 = Object.freeze({ default: ReactFiberReconciler$1 }); var ReactFiberReconciler$3 = ( ReactFiberReconciler$2 && ReactFiberReconciler$1 ) || ReactFiberReconciler$2; // TODO: bundle Flow types with the package. // TODO: decide on the top-level export form. // This is hacky but makes it work with both Rollup and Jest. var reactReconciler = ReactFiberReconciler$3['default'] ? ReactFiberReconciler$3['default'] : ReactFiberReconciler$3; function createPortal$1(children, containerInfo, // TODO: figure out the API for cross-renderer implementation. implementation) { var key = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; return { // This tag allow us to uniquely identify this as a React Portal $$typeof: REACT_PORTAL_TYPE, key: key == null ? null : '' + key, children: children, containerInfo: containerInfo, implementation: implementation }; } // TODO: this is special because it gets imported during build. var ReactVersion = '16.2.0'; // a requestAnimationFrame, storing the time for the start of the frame, then // scheduling a postMessage which gets scheduled after paint. Within the // postMessage handler do as much work as possible until time + frame rate. // By separating the idle call into a separate event tick we ensure that // layout, paint and other browser work is counted against the available time. // The frame rate is dynamically adjusted. { if (ExecutionEnvironment.canUseDOM && typeof requestAnimationFrame !== 'function') { warning(false, 'React depends on requestAnimationFrame. Make sure that you load a ' + 'polyfill in older browsers. http://fb.me/react-polyfills'); } } var hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function'; var now = void 0; if (hasNativePerformanceNow) { now = function () { return performance.now(); }; } else { now = function () { return Date.now(); }; } // TODO: There's no way to cancel, because Fiber doesn't atm. var rIC = void 0; var cIC = void 0; if (!ExecutionEnvironment.canUseDOM) { rIC = function (frameCallback) { return setTimeout(function () { frameCallback({ timeRemaining: function () { return Infinity; } }); }); }; cIC = function (timeoutID) { clearTimeout(timeoutID); }; } else if (typeof requestIdleCallback !== 'function' || typeof cancelIdleCallback !== 'function') { // Polyfill requestIdleCallback and cancelIdleCallback var scheduledRICCallback = null; var isIdleScheduled = false; var timeoutTime = -1; var isAnimationFrameScheduled = false; var frameDeadline = 0; // We start out assuming that we run at 30fps but then the heuristic tracking // will adjust this value to a faster fps if we get more frequent animation // frames. var previousFrameTime = 33; var activeFrameTime = 33; var frameDeadlineObject; if (hasNativePerformanceNow) { frameDeadlineObject = { didTimeout: false, timeRemaining: function () { // We assume that if we have a performance timer that the rAF callback // gets a performance timer value. Not sure if this is always true. var remaining = frameDeadline - performance.now(); return remaining > 0 ? remaining : 0; } }; } else { frameDeadlineObject = { didTimeout: false, timeRemaining: function () { // Fallback to Date.now() var remaining = frameDeadline - Date.now(); return remaining > 0 ? remaining : 0; } }; } // We use the postMessage trick to defer idle work until after the repaint. var messageKey = '__reactIdleCallback$' + Math.random().toString(36).slice(2); var idleTick = function (event) { if (event.source !== window || event.data !== messageKey) { return; } isIdleScheduled = false; var currentTime = now(); if (frameDeadline - currentTime <= 0) { // There's no time left in this idle period. Check if the callback has // a timeout and whether it's been exceeded. if (timeoutTime !== -1 && timeoutTime <= currentTime) { // Exceeded the timeout. Invoke the callback even though there's no // time left. frameDeadlineObject.didTimeout = true; } else { // No timeout. if (!isAnimationFrameScheduled) { // Schedule another animation callback so we retry later. isAnimationFrameScheduled = true; requestAnimationFrame(animationTick); } // Exit without invoking the callback. return; } } else { // There's still time left in this idle period. frameDeadlineObject.didTimeout = false; } timeoutTime = -1; var callback = scheduledRICCallback; scheduledRICCallback = null; if (callback !== null) { callback(frameDeadlineObject); } }; // Assumes that we have addEventListener in this environment. Might need // something better for old IE. window.addEventListener('message', idleTick, false); var animationTick = function (rafTime) { isAnimationFrameScheduled = false; var nextFrameTime = rafTime - frameDeadline + activeFrameTime; if (nextFrameTime < activeFrameTime && previousFrameTime < activeFrameTime) { if (nextFrameTime < 8) { // Defensive coding. We don't support higher frame rates than 120hz. // If we get lower than that, it is probably a bug. nextFrameTime = 8; } // If one frame goes long, then the next one can be short to catch up. // If two frames are short in a row, then that's an indication that we // actually have a higher frame rate than what we're currently optimizing. // We adjust our heuristic dynamically accordingly. For example, if we're // running on 120hz display or 90hz VR display. // Take the max of the two in case one of them was an anomaly due to // missed frame deadlines. activeFrameTime = nextFrameTime < previousFrameTime ? previousFrameTime : nextFrameTime; } else { previousFrameTime = nextFrameTime; } frameDeadline = rafTime + activeFrameTime; if (!isIdleScheduled) { isIdleScheduled = true; window.postMessage(messageKey, '*'); } }; rIC = function (callback, options) { // This assumes that we only schedule one callback at a time because that's // how Fiber uses it. scheduledRICCallback = callback; if (options != null && typeof options.timeout === 'number') { timeoutTime = now() + options.timeout; } if (!isAnimationFrameScheduled) { // If rAF didn't already schedule one, we need to schedule a frame. // TODO: If this rAF doesn't materialize because the browser throttles, we // might want to still have setTimeout trigger rIC as a backup to ensure // that we keep performing work. isAnimationFrameScheduled = true; requestAnimationFrame(animationTick); } return 0; }; cIC = function () { scheduledRICCallback = null; isIdleScheduled = false; timeoutTime = -1; }; } else { rIC = window.requestIdleCallback; cIC = window.cancelIdleCallback; } /** * Forked from fbjs/warning: * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js * * Only change is we use console.warn instead of console.error, * and do nothing when 'console' is not supported. * This really simplifies the code. * --- * Similar to invariant but only logs a warning if the condition is not met. * This can be used to log issues in development environments in critical * paths. Removing the logging code for production environments will keep the * same logic and follow the same code paths. */ var lowPriorityWarning = function () {}; { var printWarning = function (format) { for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { args[_key - 1] = arguments[_key]; } var argIndex = 0; var message = 'Warning: ' + format.replace(/%s/g, function () { return args[argIndex++]; }); if (typeof console !== 'undefined') { console.warn(message); } try { // --- Welcome to debugging React --- // This error was thrown as a convenience so that you can use this stack // to find the callsite that caused this warning to fire. throw new Error(message); } catch (x) {} }; lowPriorityWarning = function (condition, format) { if (format === undefined) { throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument'); } if (!condition) { for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { args[_key2 - 2] = arguments[_key2]; } printWarning.apply(undefined, [format].concat(args)); } }; } var lowPriorityWarning$1 = lowPriorityWarning; // isAttributeNameSafe() is currently duplicated in DOMMarkupOperations. // TODO: Find a better place for this. var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$'); var illegalAttributeNameCache = {}; var validatedAttributeNameCache = {}; function isAttributeNameSafe(attributeName) { if (validatedAttributeNameCache.hasOwnProperty(attributeName)) { return true; } if (illegalAttributeNameCache.hasOwnProperty(attributeName)) { return false; } if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) { validatedAttributeNameCache[attributeName] = true; return true; } illegalAttributeNameCache[attributeName] = true; { warning(false, 'Invalid attribute name: `%s`', attributeName); } return false; } // shouldIgnoreValue() is currently duplicated in DOMMarkupOperations. // TODO: Find a better place for this. function shouldIgnoreValue(propertyInfo, value) { return value == null || propertyInfo.hasBooleanValue && !value || propertyInfo.hasNumericValue && isNaN(value) || propertyInfo.hasPositiveNumericValue && value < 1 || propertyInfo.hasOverloadedBooleanValue && value === false; } /** * Operations for dealing with DOM properties. */ /** * Get the value for a property on a node. Only used in DEV for SSR validation. * The "expected" argument is used as a hint of what the expected value is. * Some properties have multiple equivalent values. */ function getValueForProperty(node, name, expected) { { var propertyInfo = getPropertyInfo(name); if (propertyInfo) { var mutationMethod = propertyInfo.mutationMethod; if (mutationMethod || propertyInfo.mustUseProperty) { return node[propertyInfo.propertyName]; } else { var attributeName = propertyInfo.attributeName; var stringValue = null; if (propertyInfo.hasOverloadedBooleanValue) { if (node.hasAttribute(attributeName)) { var value = node.getAttribute(attributeName); if (value === '') { return true; } if (shouldIgnoreValue(propertyInfo, expected)) { return value; } if (value === '' + expected) { return expected; } return value; } } else if (node.hasAttribute(attributeName)) { if (shouldIgnoreValue(propertyInfo, expected)) { // We had an attribute but shouldn't have had one, so read it // for the error message. return node.getAttribute(attributeName); } if (propertyInfo.hasBooleanValue) { // If this was a boolean, it doesn't matter what the value is // the fact that we have it is the same as the expected. return expected; } // Even if this property uses a namespace we use getAttribute // because we assume its namespaced name is the same as our config. // To use getAttributeNS we need the local name which we don't have // in our config atm. stringValue = node.getAttribute(attributeName); } if (shouldIgnoreValue(propertyInfo, expected)) { return stringValue === null ? expected : stringValue; } else if (stringValue === '' + expected) { return expected; } else { return stringValue; } } } } } /** * Get the value for a attribute on a node. Only used in DEV for SSR validation. * The third argument is used as a hint of what the expected value is. Some * attributes have multiple equivalent values. */ function getValueForAttribute(node, name, expected) { { if (!isAttributeNameSafe(name)) { return; } if (!node.hasAttribute(name)) { return expected === undefined ? undefined : null; } var value = node.getAttribute(name); if (value === '' + expected) { return expected; } return value; } } /** * Sets the value for a property on a node. * * @param {DOMElement} node * @param {string} name * @param {*} value */ function setValueForProperty(node, name, value) { var propertyInfo = getPropertyInfo(name); if (propertyInfo && shouldSetAttribute(name, value)) { var mutationMethod = propertyInfo.mutationMethod; if (mutationMethod) { mutationMethod(node, value); } else if (shouldIgnoreValue(propertyInfo, value)) { deleteValueForProperty(node, name); return; } else if (propertyInfo.mustUseProperty) { // Contrary to `setAttribute`, object properties are properly // `toString`ed by IE8/9. node[propertyInfo.propertyName] = value; } else { var attributeName = propertyInfo.attributeName; var namespace = propertyInfo.attributeNamespace; // `setAttribute` with objects becomes only `[object]` in IE8/9, // ('' + value) makes it output the correct toString()-value. if (namespace) { node.setAttributeNS(namespace, attributeName, '' + value); } else if (propertyInfo.hasBooleanValue || propertyInfo.hasOverloadedBooleanValue && value === true) { node.setAttribute(attributeName, ''); } else { node.setAttribute(attributeName, '' + value); } } } else { setValueForAttribute(node, name, shouldSetAttribute(name, value) ? value : null); return; } { } } function setValueForAttribute(node, name, value) { if (!isAttributeNameSafe(name)) { return; } if (value == null) { node.removeAttribute(name); } else { node.setAttribute(name, '' + value); } { } } /** * Deletes an attributes from a node. * * @param {DOMElement} node * @param {string} name */ function deleteValueForAttribute(node, name) { node.removeAttribute(name); } /** * Deletes the value for a property on a node. * * @param {DOMElement} node * @param {string} name */ function deleteValueForProperty(node, name) { var propertyInfo = getPropertyInfo(name); if (propertyInfo) { var mutationMethod = propertyInfo.mutationMethod; if (mutationMethod) { mutationMethod(node, undefined); } else if (propertyInfo.mustUseProperty) { var propName = propertyInfo.propertyName; if (propertyInfo.hasBooleanValue) { node[propName] = false; } else { node[propName] = ''; } } else { node.removeAttribute(propertyInfo.attributeName); } } else { node.removeAttribute(name); } } var ReactControlledValuePropTypes = { checkPropTypes: null }; { var hasReadOnlyValue = { button: true, checkbox: true, image: true, hidden: true, radio: true, reset: true, submit: true }; var propTypes = { value: function (props, propName, componentName) { if (!props[propName] || hasReadOnlyValue[props.type] || props.onChange || props.readOnly || props.disabled) { return null; } return new Error('You provided a `value` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultValue`. Otherwise, ' + 'set either `onChange` or `readOnly`.'); }, checked: function (props, propName, componentName) { if (!props[propName] || props.onChange || props.readOnly || props.disabled) { return null; } return new Error('You provided a `checked` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultChecked`. Otherwise, ' + 'set either `onChange` or `readOnly`.'); } }; /** * Provide a linked `value` attribute for controlled forms. You should not use * this outside of the ReactDOM controlled form components. */ ReactControlledValuePropTypes.checkPropTypes = function (tagName, props, getStack) { checkPropTypes(propTypes, props, 'prop', tagName, getStack); }; } // TODO: direct imports like some-package/src/* are bad. Fix me. var getCurrentFiberOwnerName$2 = ReactDebugCurrentFiber.getCurrentFiberOwnerName; var getCurrentFiberStackAddendum$3 = ReactDebugCurrentFiber.getCurrentFiberStackAddendum; var didWarnValueDefaultValue = false; var didWarnCheckedDefaultChecked = false; var didWarnControlledToUncontrolled = false; var didWarnUncontrolledToControlled = false; function isControlled(props) { var usesChecked = props.type === 'checkbox' || props.type === 'radio'; return usesChecked ? props.checked != null : props.value != null; } /** * Implements an host component that allows setting these optional * props: `checked`, `value`, `defaultChecked`, and `defaultValue`. * * If `checked` or `value` are not supplied (or null/undefined), user actions * that affect the checked state or value will trigger updates to the element. * * If they are supplied (and not null/undefined), the rendered element will not * trigger updates to the element. Instead, the props must change in order for * the rendered element to be updated. * * The rendered element will be initialized as unchecked (or `defaultChecked`) * with an empty value (or `defaultValue`). * * See http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html */ function getHostProps(element, props) { var node = element; var value = props.value; var checked = props.checked; var hostProps = _assign({ // Make sure we set .type before any other properties (setting .value // before .type means .value is lost in IE11 and below) type: undefined, // Make sure we set .step before .value (setting .value before .step // means .value is rounded on mount, based upon step precision) step: undefined, // Make sure we set .min & .max before .value (to ensure proper order // in corner cases such as min or max deriving from value, e.g. Issue #7170) min: undefined, max: undefined }, props, { defaultChecked: undefined, defaultValue: undefined, value: value != null ? value : node._wrapperState.initialValue, checked: checked != null ? checked : node._wrapperState.initialChecked }); return hostProps; } function initWrapperState(element, props) { { ReactControlledValuePropTypes.checkPropTypes('input', props, getCurrentFiberStackAddendum$3); if (props.checked !== undefined && props.defaultChecked !== undefined && !didWarnCheckedDefaultChecked) { warning(false, '%s contains an input of type %s with both checked and defaultChecked props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the checked prop, or the defaultChecked prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', getCurrentFiberOwnerName$2() || 'A component', props.type); didWarnCheckedDefaultChecked = true; } if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue) { warning(false, '%s contains an input of type %s with both value and defaultValue props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', getCurrentFiberOwnerName$2() || 'A component', props.type); didWarnValueDefaultValue = true; } } var defaultValue = props.defaultValue; var node = element; node._wrapperState = { initialChecked: props.checked != null ? props.checked : props.defaultChecked, initialValue: props.value != null ? props.value : defaultValue, controlled: isControlled(props) }; } function updateChecked(element, props) { var node = element; var checked = props.checked; if (checked != null) { setValueForProperty(node, 'checked', checked); } } function updateWrapper(element, props) { var node = element; { var controlled = isControlled(props); if (!node._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) { warning(false, 'A component is changing an uncontrolled input of type %s to be controlled. ' + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s', props.type, getCurrentFiberStackAddendum$3()); didWarnUncontrolledToControlled = true; } if (node._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) { warning(false, 'A component is changing a controlled input of type %s to be uncontrolled. ' + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components%s', props.type, getCurrentFiberStackAddendum$3()); didWarnControlledToUncontrolled = true; } } updateChecked(element, props); var value = props.value; if (value != null) { if (value === 0 && node.value === '') { node.value = '0'; // Note: IE9 reports a number inputs as 'text', so check props instead. } else if (props.type === 'number') { // Simulate `input.valueAsNumber`. IE9 does not support it var valueAsNumber = parseFloat(node.value) || 0; if ( // eslint-disable-next-line value != valueAsNumber || // eslint-disable-next-line value == valueAsNumber && node.value != value) { // Cast `value` to a string to ensure the value is set correctly. While // browsers typically do this as necessary, jsdom doesn't. node.value = '' + value; } } else if (node.value !== '' + value) { // Cast `value` to a string to ensure the value is set correctly. While // browsers typically do this as necessary, jsdom doesn't. node.value = '' + value; } } else { if (props.value == null && props.defaultValue != null) { // In Chrome, assigning defaultValue to certain input types triggers input validation. // For number inputs, the display value loses trailing decimal points. For email inputs, // Chrome raises "The specified value is not a valid email address". // // Here we check to see if the defaultValue has actually changed, avoiding these problems // when the user is inputting text // // https://github.com/facebook/react/issues/7253 if (node.defaultValue !== '' + props.defaultValue) { node.defaultValue = '' + props.defaultValue; } } if (props.checked == null && props.defaultChecked != null) { node.defaultChecked = !!props.defaultChecked; } } } function postMountWrapper(element, props) { var node = element; // Detach value from defaultValue. We won't do anything if we're working on // submit or reset inputs as those values & defaultValues are linked. They // are not resetable nodes so this operation doesn't matter and actually // removes browser-default values (eg "Submit Query") when no value is // provided. switch (props.type) { case 'submit': case 'reset': break; case 'color': case 'date': case 'datetime': case 'datetime-local': case 'month': case 'time': case 'week': // This fixes the no-show issue on iOS Safari and Android Chrome: // https://github.com/facebook/react/issues/7233 node.value = ''; node.value = node.defaultValue; break; default: node.value = node.value; break; } // Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug // this is needed to work around a chrome bug where setting defaultChecked // will sometimes influence the value of checked (even after detachment). // Reference: https://bugs.chromium.org/p/chromium/issues/detail?id=608416 // We need to temporarily unset name to avoid disrupting radio button groups. var name = node.name; if (name !== '') { node.name = ''; } node.defaultChecked = !node.defaultChecked; node.defaultChecked = !node.defaultChecked; if (name !== '') { node.name = name; } } function restoreControlledState$1(element, props) { var node = element; updateWrapper(node, props); updateNamedCousins(node, props); } function updateNamedCousins(rootNode, props) { var name = props.name; if (props.type === 'radio' && name != null) { var queryRoot = rootNode; while (queryRoot.parentNode) { queryRoot = queryRoot.parentNode; } // If `rootNode.form` was non-null, then we could try `form.elements`, // but that sometimes behaves strangely in IE8. We could also try using // `form.getElementsByName`, but that will only return direct children // and won't include inputs that use the HTML5 `form=` attribute. Since // the input might not even be in a form. It might not even be in the // document. Let's just use the local `querySelectorAll` to ensure we don't // miss anything. var group = queryRoot.querySelectorAll('input[name=' + JSON.stringify('' + name) + '][type="radio"]'); for (var i = 0; i < group.length; i++) { var otherNode = group[i]; if (otherNode === rootNode || otherNode.form !== rootNode.form) { continue; } // This will throw if radio buttons rendered by different copies of React // and the same name are rendered into the same form (same as #1939). // That's probably okay; we don't support it just as we don't support // mixing React radio buttons with non-React ones. var otherProps = getFiberCurrentPropsFromNode$1(otherNode); !otherProps ? invariant(false, 'ReactDOMInput: Mixing React and non-React radio inputs with the same `name` is not supported.') : void 0; // We need update the tracked value on the named cousin since the value // was changed but the input saw no event or value set updateValueIfChanged(otherNode); // If this is a controlled radio button group, forcing the input that // was previously checked to update will cause it to be come re-checked // as appropriate. updateWrapper(otherNode, otherProps); } } } function flattenChildren(children) { var content = ''; // Flatten children and warn if they aren't strings or numbers; // invalid types are ignored. // We can silently skip them because invalid DOM nesting warning // catches these cases in Fiber. React.Children.forEach(children, function (child) { if (child == null) { return; } if (typeof child === 'string' || typeof child === 'number') { content += child; } }); return content; } /** * Implements an