From d676746f14fb6d714d2576871655b13c005480e7 Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Mon, 29 Jan 2018 14:17:07 -0800 Subject: [PATCH] React sync for revisions 9491dee...a7b9f98 Reviewed By: sebmarkbage Differential Revision: D6834573 fbshipit-source-id: 30829313053ecec54a891941fcf090021497ef8e --- Libraries/Renderer/REVISION | 2 +- Libraries/Renderer/ReactNativeRenderer-dev.js | 16928 ++++++++-------- .../Renderer/ReactNativeRenderer-prod.js | 2062 +- Libraries/Renderer/shims/ReactFeatureFlags.js | 6 +- Libraries/Renderer/shims/ReactTypes.js | 75 +- 5 files changed, 9940 insertions(+), 9133 deletions(-) diff --git a/Libraries/Renderer/REVISION b/Libraries/Renderer/REVISION index 3b41417678220a..7a75297ceafe15 100644 --- a/Libraries/Renderer/REVISION +++ b/Libraries/Renderer/REVISION @@ -1 +1 @@ -9491dee79586d21c115cd4d5986c81b7d88d2b3f \ No newline at end of file +a7b9f98e7abab7305978b4b36881c8267a450097 \ No newline at end of file diff --git a/Libraries/Renderer/ReactNativeRenderer-dev.js b/Libraries/Renderer/ReactNativeRenderer-dev.js index e9dcd9a2985bb7..64ed9dba80bab7 100644 --- a/Libraries/Renderer/ReactNativeRenderer-dev.js +++ b/Libraries/Renderer/ReactNativeRenderer-dev.js @@ -22,119 +22,24 @@ var emptyFunction = require("fbjs/lib/emptyFunction"); var RCTEventEmitter = require("RCTEventEmitter"); var UIManager = require("UIManager"); var React = require("react"); -var ExceptionsManager = require("ExceptionsManager"); var TextInputState = require("TextInputState"); var deepDiffer = require("deepDiffer"); var flattenStyle = require("flattenStyle"); var emptyObject = require("fbjs/lib/emptyObject"); var checkPropTypes = require("prop-types/checkPropTypes"); var shallowEqual = require("fbjs/lib/shallowEqual"); +var ExceptionsManager = require("ExceptionsManager"); var deepFreezeAndThrowOnMutationInDev = require("deepFreezeAndThrowOnMutationInDev"); -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) { - invariant( - typeof injectedErrorUtils.invokeGuardedCallback === "function", - "Injected invokeGuardedCallback() must be a function." - ); - 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; + this._hasCaughtError = false; + this._caughtError = null; var funcArgs = Array.prototype.slice.call(arguments, 3); try { func.apply(context, funcArgs); } catch (error) { - ReactErrorUtils._caughtError = error; - ReactErrorUtils._hasCaughtError = true; + this._caughtError = error; + this._hasCaughtError = true; } }; @@ -273,11 +178,11 @@ var invokeGuardedCallback = function(name, func, context, a, b, c, d, e, f) { "See https://fb.me/react-crossorigin-error for more information." ); } - ReactErrorUtils._hasCaughtError = true; - ReactErrorUtils._caughtError = error; + this._hasCaughtError = true; + this._caughtError = error; } else { - ReactErrorUtils._hasCaughtError = false; - ReactErrorUtils._caughtError = null; + this._hasCaughtError = false; + this._caughtError = null; } // Remove our event listeners @@ -288,6 +193,93 @@ var invokeGuardedCallback = function(name, func, context, a, b, c, d, e, f) { } } +var invokeGuardedCallback$1 = invokeGuardedCallback; + +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, + + /** + * 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$1.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 rethrowCaughtError = function() { if (ReactErrorUtils._hasRethrowError) { var error = ReactErrorUtils._rethrowError; @@ -297,6 +289,78 @@ var rethrowCaughtError = function() { } }; +/** + * 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; + +var shouldWarnOnInjection = false; + /** * Injectable ordering of event plugins. */ @@ -482,6 +546,21 @@ function injectEventPluginOrder(injectedEventPluginOrder) { * @see {EventPluginHub.injection.injectEventPluginsByName} */ function injectEventPluginsByName(injectedNamesToPlugins) { + { + if (shouldWarnOnInjection) { + var names = Object.keys(injectedNamesToPlugins).join(", "); + lowPriorityWarning$1( + false, + "Injecting custom event plugins (%s) is deprecated " + + "and will not work in React 17+. Please update your code " + + "to not depend on React internals. The stack trace for this " + + "warning should reveal the library that is using them. " + + "See https://github.com/facebook/react/issues/11689 for a discussion.", + names + ); + } + } + var isOrderingDirty = false; for (var pluginName in injectedNamesToPlugins) { if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) { @@ -542,7 +621,7 @@ function isStartish(topLevelType) { return topLevelType === "topMouseDown" || topLevelType === "topTouchStart"; } -var validateEventDispatches; +var validateEventDispatches = void 0; { validateEventDispatches = function(event) { var dispatchListeners = event._dispatchListeners; @@ -851,7 +930,7 @@ var injection = { * @return {?function} The stored callback. */ function getListener(inst, registrationName) { - var listener; + var listener = void 0; // TODO: shouldPreventMouseEvent is DOM-specific and definitely should not // live here; needs to be moved to a better place soon @@ -891,7 +970,7 @@ function extractEvents( nativeEvent, nativeEventTarget ) { - var events; + var events = null; for (var i = 0; i < plugins.length; i++) { // Not every plugin in the ordering may be loaded at runtime. var possiblePlugin = plugins[i]; @@ -910,25 +989,11 @@ function extractEvents( 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) { +function runEventsInBatch(events, simulated) { + if (events !== null) { 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; @@ -958,6 +1023,21 @@ function processEventQueue(simulated) { ReactErrorUtils.rethrowCaughtError(); } +function runExtractedEventsInBatch( + topLevelType, + targetInst, + nativeEvent, + nativeEventTarget +) { + var events = extractEvents( + topLevelType, + targetInst, + nativeEvent, + nativeEventTarget + ); + runEventsInBatch(events, false); +} + var IndeterminateComponent = 0; // Before we know whether it is functional or class var FunctionalComponent = 1; var ClassComponent = 2; @@ -969,6 +1049,9 @@ var CallComponent = 7; var CallHandlerPhase = 8; var ReturnComponent = 9; var Fragment = 10; +var Mode = 11; +var ContextConsumer = 12; +var ContextProvider = 13; function getParent(inst) { do { @@ -1052,7 +1135,7 @@ function traverseTwoPhase(inst, fn, arg) { path.push(inst); inst = getParent(inst); } - var i; + var i = void 0; for (i = path.length; i-- > 0; ) { fn(path[i], "captured", arg); } @@ -1178,7 +1261,6 @@ function accumulateDirectDispatches(events) { /* eslint valid-typeof: 0 */ var didWarnForAddedNewProperty = false; -var isProxySupported = typeof Proxy === "function"; var EVENT_POOL_SIZE = 10; var shouldBeReleasedProperties = [ @@ -1371,24 +1453,26 @@ SyntheticEvent.Interface = EventInterface; /** * Helper to reduce boilerplate when creating subclasses. - * - * @param {function} Class - * @param {?object} Interface */ -SyntheticEvent.augmentClass = function(Class, Interface) { +SyntheticEvent.extend = function(Interface) { var Super = this; var E = function() {}; E.prototype = Super.prototype; var prototype = new E(); + function Class() { + return Super.apply(this, arguments); + } Object.assign(prototype, Class.prototype); Class.prototype = prototype; Class.prototype.constructor = Class; Class.Interface = Object.assign({}, Super.Interface, Interface); - Class.augmentClass = Super.augmentClass; + Class.extend = Super.extend; addEventPoolingTo(Class); + + return Class; }; /** Proxying after everything set on SyntheticEvent @@ -1396,6 +1480,11 @@ SyntheticEvent.augmentClass = function(Class, Interface) { * in which some Event properties are set to undefined (GH#10010) */ { + var isProxySupported = + typeof Proxy === "function" && + // https://github.com/facebook/react/issues/12011 + !Object.isSealed(new Proxy({}, {})); + if (isProxySupported) { /*eslint-disable no-func-assign */ SyntheticEvent = new Proxy(SyntheticEvent, { @@ -1522,34 +1611,11 @@ var SyntheticEvent$1 = SyntheticEvent; * interface will ensure that it is cleaned up when pooled/destroyed. The * `ResponderEventPlugin` will populate it appropriately. */ -var ResponderEventInterface = { +var ResponderSyntheticEvent = SyntheticEvent$1.extend({ touchHistory: function(nativeEvent) { return null; // Actually doesn't even look at the native event. } -}; - -/** - * @param {object} dispatchConfig Configuration used to dispatch this event. - * @param {string} dispatchMarker Marker identifying the event target. - * @param {object} nativeEvent Native event. - * @extends {SyntheticEvent} - */ -function ResponderSyntheticEvent( - dispatchConfig, - dispatchMarker, - nativeEvent, - nativeEventTarget -) { - return SyntheticEvent$1.call( - this, - dispatchConfig, - dispatchMarker, - nativeEvent, - nativeEventTarget - ); -} - -SyntheticEvent$1.augmentClass(ResponderSyntheticEvent, ResponderEventInterface); +}); /** * Tracks the position and time of each active touch by `touch.identifier`. We @@ -2091,7 +2157,7 @@ function setResponderAndExtractTransfer( if (!wantsResponderInst || wantsResponderInst === responderInst) { return null; } - var extracted; + var extracted = void 0; var grantEvent = ResponderSyntheticEvent.getPooled( eventTypes.responderGrant, wantsResponderInst, @@ -2420,11 +2486,19 @@ function uncacheFiberNode(tag) { } function getInstanceFromTag(tag) { - return instanceCache[tag] || null; + if (typeof tag === "number") { + return instanceCache[tag] || null; + } else { + // Fabric will invoke event emitters on a direct fiber reference + return tag; + } } function getTagFromInstance(inst) { var tag = inst.stateNode._nativeTag; + if (tag === undefined) { + tag = inst.stateNode.canonical._nativeTag; + } invariant(tag, "All native instances should have a tag."); return tag; } @@ -2533,30 +2607,6 @@ var ReactGenericBatchingInjection = { var injection$2 = ReactGenericBatchingInjection; -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); -} - /** * Keeps track of allocating and associating native "tags" which are numeric, * unique view IDs. All the native tags are negative numbers, to avoid @@ -2668,7 +2718,12 @@ function _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam) { var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT; var inst = getInstanceFromTag(rootNodeID); batchedUpdates(function() { - handleTopLevel(topLevelType, inst, nativeEvent, nativeEvent.target); + runExtractedEventsInBatch( + topLevelType, + inst, + nativeEvent, + nativeEvent.target + ); }); // React Native doesn't use ReactControlledComponent but if it did, here's // where it would do it. @@ -2747,8 +2802,7 @@ var ReactNativeEventEmitter = Object.freeze({ registrationNames: registrationNameModules, _receiveRootNodeIDEvent: _receiveRootNodeIDEvent, receiveEvent: receiveEvent, - receiveTouches: receiveTouches, - handleTopLevel: handleTopLevel + receiveTouches: receiveTouches }); var ReactNativeEventPluginOrder = [ @@ -2800,90 +2854,6 @@ injection.injectEventPluginsByName({ ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin }); -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 injection$4 = { - /** - * Display custom dialog for lifecycle errors. - * Return false to prevent default behavior of logging to console.error. - */ - injectDialog: function(fn) { - invariant( - showDialog === defaultShowDialog, - "The custom dialog was already injected." - ); - invariant( - typeof fn === "function", - "Injected showDialog() must be a function." - ); - showDialog = fn; - } -}; - // 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"]; @@ -2893,6 +2863,11 @@ 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 REACT_STRICT_MODE_TYPE = hasSymbol + ? Symbol["for"]("react.strict_mode") + : 0xeacc; +var REACT_PROVIDER_TYPE = hasSymbol ? Symbol["for"]("react.provider") : 0xeacd; +var REACT_CONTEXT_TYPE = hasSymbol ? Symbol["for"]("react.context") : 0xeace; var MAYBE_ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator; var FAUX_ITERATOR_SYMBOL = "@@iterator"; @@ -2985,7 +2960,7 @@ var TouchHistoryMath = { touchTrack.touchActive && touchTrack.currentTimeStamp >= touchesChangedAfter ) { - var toAdd; // Yuck, program temporarily in invalid state. + var toAdd = void 0; // Yuck, program temporarily in invalid state. if (ofCurrent && isXAxis) { toAdd = touchTrack.currentPageX; } else if (ofCurrent && !isXAxis) { @@ -3086,47 +3061,6 @@ var ReactGlobalSharedState = Object.freeze({ var ReactVersion = "16.2.0"; -// Module provided by RN: -/** - * Intercept lifecycle errors and ensure they are shown with the correct stack - * trace within the native redbox component. - */ -function showDialog$1(capturedError) { - var componentStack = capturedError.componentStack, - error = capturedError.error; - - var errorToHandle = void 0; - - // Typically Errors are thrown but eg strings or null can be thrown as well. - if (error instanceof Error) { - var message = error.message, - name = error.name; - - var summary = message ? name + ": " + message : name; - - errorToHandle = error; - - try { - errorToHandle.message = - summary + "\n\nThis error is located at:" + componentStack; - } catch (e) {} - } else if (typeof error === "string") { - errorToHandle = new Error( - error + "\n\nThis error is located at:" + componentStack - ); - } else { - errorToHandle = new Error("Unspecified error at:" + componentStack); - } - - ExceptionsManager.handleException(errorToHandle, false); - - // Return false here to prevent ReactFiberErrorLogger default behavior of - // logging error details to console.error. Calls to console.error are - // automatically routed to the native redbox controller, which we've already - // done above by calling ExceptionsManager. - return false; -} - function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); @@ -3222,8 +3156,8 @@ function restoreDeletedValuesInNestedArray( if (!removedKeys[propKey]) { continue; } - var nextProp = obj[propKey]; - if (nextProp === undefined) { + var _nextProp = obj[propKey]; + if (_nextProp === undefined) { continue; } @@ -3232,16 +3166,16 @@ function restoreDeletedValuesInNestedArray( continue; // not a valid native prop } - if (typeof nextProp === "function") { - nextProp = true; + if (typeof _nextProp === "function") { + _nextProp = true; } - if (typeof nextProp === "undefined") { - nextProp = null; + if (typeof _nextProp === "undefined") { + _nextProp = null; } if (typeof attributeConfig !== "object") { // case: !Object is the default case - updatePayload[propKey] = nextProp; + updatePayload[propKey] = _nextProp; } else if ( typeof attributeConfig.diff === "function" || typeof attributeConfig.process === "function" @@ -3249,8 +3183,8 @@ function restoreDeletedValuesInNestedArray( // case: CustomAttributeConfiguration var nextValue = typeof attributeConfig.process === "function" - ? attributeConfig.process(nextProp) - : nextProp; + ? attributeConfig.process(_nextProp) + : _nextProp; updatePayload[propKey] = nextValue; } removedKeys[propKey] = false; @@ -3267,7 +3201,7 @@ function diffNestedArrayProperty( ) { var minLength = prevArray.length < nextArray.length ? prevArray.length : nextArray.length; - var i; + var i = void 0; for (i = 0; i < minLength; i++) { // Diff any items in the array in the forward direction. Repeated keys // will be overwritten by later values. @@ -3426,9 +3360,9 @@ function clearNestedProperty(updatePayload, prevProp, validAttributes) { * anything changed. */ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) { - var attributeConfig; - var nextProp; - var prevProp; + var attributeConfig = void 0; + var nextProp = void 0; + var prevProp = void 0; for (var propKey in nextProps) { attributeConfig = validAttributes[propKey]; @@ -3509,11 +3443,11 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) { ? attributeConfig.diff(prevProp, nextProp) : defaultDiffer(prevProp, nextProp)); if (shouldUpdate) { - nextValue = + var _nextValue = typeof attributeConfig.process === "function" ? attributeConfig.process(nextProp) : nextProp; - (updatePayload || (updatePayload = {}))[propKey] = nextValue; + (updatePayload || (updatePayload = {}))[propKey] = _nextValue; } } else { // default: fallthrough case when nested properties are defined @@ -3541,21 +3475,21 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) { // Also iterate through all the previous props to catch any that have been // removed and make sure native gets the signal so it can reset them to the // default. - for (propKey in prevProps) { - if (nextProps[propKey] !== undefined) { + for (var _propKey in prevProps) { + if (nextProps[_propKey] !== undefined) { continue; // we've already covered this key in the previous pass } - attributeConfig = validAttributes[propKey]; + attributeConfig = validAttributes[_propKey]; if (!attributeConfig) { continue; // not a valid native prop } - if (updatePayload && updatePayload[propKey] !== undefined) { + if (updatePayload && updatePayload[_propKey] !== undefined) { // This was already updated to a diff result earlier. continue; } - prevProp = prevProps[propKey]; + prevProp = prevProps[_propKey]; if (prevProp === undefined) { continue; // was already empty anyway } @@ -3567,12 +3501,12 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) { ) { // case: CustomAttributeConfiguration | !Object // Flag the leaf property for removal by sending a sentinel. - (updatePayload || (updatePayload = {}))[propKey] = null; + (updatePayload || (updatePayload = {}))[_propKey] = null; if (!removedKeys) { removedKeys = {}; } - if (!removedKeys[propKey]) { - removedKeys[propKey] = true; + if (!removedKeys[_propKey]) { + removedKeys[_propKey] = true; removedKeyCount++; } } else { @@ -3719,5193 +3653,5772 @@ function set(key, value) { function getComponentName(fiber) { var type = fiber.type; + if (typeof type === "function") { + return type.displayName || type.name; + } if (typeof type === "string") { return type; } - if (typeof type === "function") { - return type.displayName || type.name; + switch (type) { + case REACT_FRAGMENT_TYPE: + return "ReactFragment"; + case REACT_PORTAL_TYPE: + return "ReactPortal"; + case REACT_CALL_TYPE: + return "ReactCall"; + case REACT_RETURN_TYPE: + return "ReactReturn"; } return null; } -// Re-export dynamic flags from the fbsource version. -var _require = require("ReactFeatureFlags"); - -var debugRenderPhaseSideEffects = _require.debugRenderPhaseSideEffects; - -var enableAsyncSubtreeAPI = true; - -var enableUserTimingAPI = true; -var enableMutatingReconciler = true; -var enableNoopReconciler = false; -var enablePersistentReconciler = false; +// TODO: Share this module between Fabric and React Native renderers +// so that both can be used in the same tree. -// Only used in www builds. - -// 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 findHostInstance = function(fiber) { + return null; +}; -var MOUNTING = 1; -var MOUNTED = 2; -var UNMOUNTED = 3; +var findHostInstanceFabric = function(fiber) { + return null; +}; -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 injectFindHostInstance(impl) { + findHostInstance = impl; } -function isFiberMounted(fiber) { - return isFiberMountedImpl(fiber) === MOUNTED; -} +/** + * ReactNative vs ReactWeb + * ----------------------- + * React treats some pieces of data opaquely. This means that the information + * is first class (it can be passed around), but cannot be inspected. This + * allows us to build infrastructure that reasons about resources, without + * making assumptions about the nature of those resources, and this allows that + * infra to be shared across multiple platforms, where the resources are very + * different. General infra (such as `ReactMultiChild`) reasons opaquely about + * the data, but platform specific code (such as `ReactNativeBaseComponent`) can + * make assumptions about the data. + * + * + * `rootNodeID`, uniquely identifies a position in the generated native view + * tree. Many layers of composite components (created with `React.createClass`) + * can all share the same `rootNodeID`. + * + * `nodeHandle`: A sufficiently unambiguous way to refer to a lower level + * resource (dom node, native view etc). The `rootNodeID` is sufficient for web + * `nodeHandle`s, because the position in a tree is always enough to uniquely + * identify a DOM node (we never have nodes in some bank outside of the + * document). The same would be true for `ReactNative`, but we must maintain a + * mapping that we can send efficiently serializable + * strings across native boundaries. + * + * Opaque name TodaysWebReact FutureWebWorkerReact ReactNative + * ---------------------------------------------------------------------------- + * nodeHandle N/A rootNodeID tag + */ -function isMounted(component) { +// TODO (bvaughn) Rename the findNodeHandle module to something more descriptive +// eg findInternalHostInstance. This will reduce the likelihood of someone +// accidentally deep-requiring this version. +function findNodeHandle(componentOrHandle) { { var owner = ReactCurrentOwner.current; - if (owner !== null && owner.tag === ClassComponent) { - var ownerFiber = owner; - var instance = ownerFiber.stateNode; + if (owner !== null && owner.stateNode !== null) { warning( - instance._warnedAboutRefsInRender, - "%s is accessing isMounted inside its render() function. " + + owner.stateNode._warnedAboutRefsInRender, + "%s is accessing findNodeHandle inside its render(). " + "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" + getComponentName(owner) || "A component" ); - instance._warnedAboutRefsInRender = true; + + owner.stateNode._warnedAboutRefsInRender = true; } } - - var fiber = get(component); - if (!fiber) { - return false; + if (componentOrHandle == null) { + return null; + } + if (typeof componentOrHandle === "number") { + // Already a node handle + return componentOrHandle; } - return isFiberMountedImpl(fiber) === MOUNTED; -} -function assertIsMounted(fiber) { - invariant( - isFiberMountedImpl(fiber) === MOUNTED, - "Unable to find node on an unmounted component." - ); -} + var component = componentOrHandle; -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); - invariant( - state !== UNMOUNTED, - "Unable to find node on an unmounted component." + // TODO (balpert): Wrap iOS native components in a composite wrapper, then + // ReactInstanceMap.get here will always succeed for mounted components + var internalInstance = get(component); + if (internalInstance) { + return ( + findHostInstance(internalInstance) || + findHostInstanceFabric(internalInstance) ); - 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 { + if (component) { + return component; } 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; - } - invariant( - didFindChild, - "Child was not found in either parent set. This indicates a bug " + - "in React related to the return pointer. Please file an issue." - ); - } + invariant( + // Native + (typeof component === "object" && "_nativeTag" in component) || + // Composite + (component.render != null && typeof component.render === "function"), + "findNodeHandle(...): Argument is not a component " + + "(type: %s, keys: %s)", + typeof component, + Object.keys(component) + ); + invariant( + false, + "findNodeHandle(...): Unable to find node handle for unmounted " + + "component." + ); } - - invariant( - a.alternate === b, - "Return fibers should always be each others' alternates. " + - "This error is likely caused by a bug in React. Please file an issue." - ); - } - // If the root is not a host container, we're in a disconnected tree. I.e. - // unmounted. - invariant( - a.tag === HostRoot, - "Unable to find node on an unmounted component." - ); - 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; +/** + * External users of findNodeHandle() expect the host tag number return type. + * The injected findNodeHandle() strategy returns the instance wrapper though. + * See NativeMethodsMixin#setNativeProps for more info on why this is done. + */ +function findNumericNodeHandleFiber(componentOrHandle) { + var instance = findNodeHandle(componentOrHandle); + if (instance == null || typeof instance === "number") { + return instance; } - // Flow needs the return null here, but ESLint complains about it. - // eslint-disable-next-line no-unreachable - return null; + return instance._nativeTag; } -function findCurrentHostFiberWithNoPortals(parent) { - var currentParent = findCurrentFiberUsingSlowPath(parent); - if (!currentParent) { - return null; - } +// Modules provided by RN: +/** + * `NativeMethodsMixin` provides methods to access the underlying native + * component directly. This can be useful in cases when you want to focus + * a view or measure its on-screen dimensions, for example. + * + * The methods described here are available on most of the default components + * provided by React Native. Note, however, that they are *not* available on + * composite components that aren't directly backed by a native view. This will + * generally include most components that you define in your own app. For more + * information, see [Direct + * Manipulation](docs/direct-manipulation.html). + * + * Note the Flow $Exact<> syntax is required to support mixins. + * React createClass mixins can only be used with exact types. + */ +var NativeMethodsMixin = { + /** + * Determines the location on screen, width, and height of the given view and + * returns the values via an async callback. If successful, the callback will + * be called with the following arguments: + * + * - x + * - y + * - width + * - height + * - pageX + * - pageY + * + * Note that these measurements are not available until after the rendering + * has been completed in native. If you need the measurements as soon as + * possible, consider using the [`onLayout` + * prop](docs/view.html#onlayout) instead. + */ + measure: function(callback) { + UIManager.measure( + findNumericNodeHandleFiber(this), + mountSafeCallback(this, callback) + ); + }, - // 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; -} + /** + * Determines the location of the given view in the window and returns the + * values via an async callback. If the React root view is embedded in + * another native view, this will give you the absolute coordinates. If + * successful, the callback will be called with the following + * arguments: + * + * - x + * - y + * - width + * - height + * + * Note that these measurements are not available until after the rendering + * has been completed in native. + */ + measureInWindow: function(callback) { + UIManager.measureInWindow( + findNumericNodeHandleFiber(this), + mountSafeCallback(this, callback) + ); + }, -var valueStack = []; + /** + * Like [`measure()`](#measure), but measures the view relative an ancestor, + * specified as `relativeToNativeNode`. This means that the returned x, y + * are relative to the origin x, y of the ancestor view. + * + * As always, to obtain a native node handle for a component, you can use + * `findNumericNodeHandle(component)`. + */ + measureLayout: function( + relativeToNativeNode, + onSuccess, + onFail /* currently unused */ + ) { + UIManager.measureLayout( + findNumericNodeHandleFiber(this), + relativeToNativeNode, + mountSafeCallback(this, onFail), + mountSafeCallback(this, onSuccess) + ); + }, -{ - var fiberStack = []; -} + /** + * This function sends props straight to native. They will not participate in + * future diff process - this means that if you do not include them in the + * next render, they will remain active (see [Direct + * Manipulation](docs/direct-manipulation.html)). + */ + setNativeProps: function(nativeProps) { + // Class components don't have viewConfig -> validateAttributes. + // Nor does it make sense to set native props on a non-native component. + // Instead, find the nearest host component and set props on it. + // Use findNodeHandle() rather than findNumericNodeHandle() because + // We want the instance/wrapper (not the native tag). + var maybeInstance = void 0; -var index = -1; + // Fiber errors if findNodeHandle is called for an umounted component. + // Tests using ReactTestRenderer will trigger this case indirectly. + // Mimicking stack behavior, we should silently ignore this case. + // TODO Fix ReactTestRenderer so we can remove this try/catch. + try { + maybeInstance = findNodeHandle(this); + } catch (error) {} -function createCursor(defaultValue) { - return { - current: defaultValue - }; -} + // If there is no host component beneath this we should fail silently. + // This is not an error; it could mean a class component rendered null. + if (maybeInstance == null) { + return; + } + + var viewConfig = maybeInstance.viewConfig; -function pop(cursor, fiber) { - if (index < 0) { { - warning(false, "Unexpected pop."); + warnForStyleProps(nativeProps, viewConfig.validAttributes); } - return; - } - { - if (fiber !== fiberStack[index]) { - warning(false, "Unexpected Fiber popped."); - } - } + var updatePayload = create(nativeProps, viewConfig.validAttributes); - cursor.current = valueStack[index]; + // Avoid the overhead of bridge calls if there's no update. + // This is an expensive no-op for Android, and causes an unnecessary + // view invalidation for certain components (eg RCTTextInput) on iOS. + if (updatePayload != null) { + UIManager.updateView( + maybeInstance._nativeTag, + viewConfig.uiViewClassName, + updatePayload + ); + } + }, - valueStack[index] = null; + /** + * Requests focus for the given input or view. The exact behavior triggered + * will depend on the platform and type of view. + */ + focus: function() { + TextInputState.focusTextInput(findNumericNodeHandleFiber(this)); + }, - { - fiberStack[index] = null; + /** + * Removes focus from an input or view. This is the opposite of `focus()`. + */ + blur: function() { + TextInputState.blurTextInput(findNumericNodeHandleFiber(this)); } +}; - index--; +{ + // hide this from Flow since we can't define these properties outside of + // true without actually implementing them (setting them to undefined + // isn't allowed by ReactClass) + var NativeMethodsMixin_DEV = NativeMethodsMixin; + invariant( + !NativeMethodsMixin_DEV.componentWillMount && + !NativeMethodsMixin_DEV.componentWillReceiveProps && + !NativeMethodsMixin_DEV.UNSAFE_componentWillMount && + !NativeMethodsMixin_DEV.UNSAFE_componentWillReceiveProps, + "Do not override existing functions." + ); + NativeMethodsMixin_DEV.componentWillMount = function() { + throwOnStylesProp(this, this.props); + }; + NativeMethodsMixin_DEV.componentWillReceiveProps = function(newProps) { + throwOnStylesProp(this, newProps); + }; + NativeMethodsMixin_DEV.UNSAFE_componentWillMount = function() { + throwOnStylesProp(this, this.props); + }; + NativeMethodsMixin_DEV.UNSAFE_componentWillReceiveProps = function(newProps) { + throwOnStylesProp(this, newProps); + }; } -function push(cursor, value, fiber) { - index++; - - valueStack[index] = cursor.current; - - { - fiberStack[index] = fiber; +function _classCallCheck$1(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); } - - cursor.current = value; } -function reset() { - while (index > -1) { - valueStack[index] = null; - - { - fiberStack[index] = null; - } - - index--; +function _possibleConstructorReturn(self, call) { + if (!self) { + throw new ReferenceError( + "this hasn't been initialised - super() hasn't been called" + ); } + return call && (typeof call === "object" || typeof call === "function") + ? call + : self; } -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 ""; +function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError( + "Super expression must either be null or a function, not " + + typeof superClass + ); } + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: false, + writable: true, + configurable: true + } + }); + if (superClass) + Object.setPrototypeOf + ? Object.setPrototypeOf(subClass, superClass) + : (subClass.__proto__ = superClass); } -// 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; -} +// Modules provided by RN: +/** + * Superclass that provides methods to access the underlying native component. + * This can be useful when you want to focus a view or measure its dimensions. + * + * Methods implemented by this class are available on most default components + * provided by React Native. However, they are *not* available on composite + * components that are not directly backed by a native view. For more + * information, see [Direct Manipulation](docs/direct-manipulation.html). + * + * @abstract + */ -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; -} +var ReactNativeComponent = (function(_React$Component) { + _inherits(ReactNativeComponent, _React$Component); -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); + function ReactNativeComponent() { + _classCallCheck$1(this, ReactNativeComponent); + + return _possibleConstructorReturn( + this, + _React$Component.apply(this, arguments) + ); } - return null; -} -function resetCurrentFiber() { - ReactDebugCurrentFrame.getCurrentStack = null; - ReactDebugCurrentFiber.current = null; - ReactDebugCurrentFiber.phase = null; -} + /** + * Removes focus. This is the opposite of `focus()`. + */ + ReactNativeComponent.prototype.blur = function blur() { + TextInputState.blurTextInput(findNumericNodeHandleFiber(this)); + }; -function setCurrentFiber(fiber) { - ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackAddendum; - ReactDebugCurrentFiber.current = fiber; - ReactDebugCurrentFiber.phase = null; -} + /** + * Requests focus. The exact behavior depends on the platform and view. + */ -function setCurrentPhase(phase) { - ReactDebugCurrentFiber.phase = phase; -} + ReactNativeComponent.prototype.focus = function focus() { + TextInputState.focusTextInput(findNumericNodeHandleFiber(this)); + }; -var ReactDebugCurrentFiber = { - current: null, - phase: null, - resetCurrentFiber: resetCurrentFiber, - setCurrentFiber: setCurrentFiber, - setCurrentPhase: setCurrentPhase, - getCurrentFiberOwnerName: getCurrentFiberOwnerName, - getCurrentFiberStackAddendum: getCurrentFiberStackAddendum -}; + /** + * Measures the on-screen location and dimensions. If successful, the callback + * will be called asynchronously with the following arguments: + * + * - x + * - y + * - width + * - height + * - pageX + * - pageY + * + * These values are not available until after natives rendering completes. If + * you need the measurements as soon as possible, consider using the + * [`onLayout` prop](docs/view.html#onlayout) instead. + */ -// 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"; + ReactNativeComponent.prototype.measure = function measure(callback) { + UIManager.measure( + findNumericNodeHandleFiber(this), + mountSafeCallback(this, callback) + ); + }; -// 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(); + /** + * Measures the on-screen location and dimensions. Even if the React Native + * root view is embedded within another native view, this method will give you + * the absolute coordinates measured from the window. If successful, the + * callback will be called asynchronously with the following arguments: + * + * - x + * - y + * - width + * - height + * + * These values are not available until after natives rendering completes. + */ -var formatMarkName = function(markName) { - return reactEmoji + " " + markName; -}; + ReactNativeComponent.prototype.measureInWindow = function measureInWindow( + callback + ) { + UIManager.measureInWindow( + findNumericNodeHandleFiber(this), + mountSafeCallback(this, callback) + ); + }; -var formatLabel = function(label, warning$$1) { - var prefix = warning$$1 ? warningEmoji + " " : reactEmoji + " "; - var suffix = warning$$1 ? " Warning: " + warning$$1 : ""; - return "" + prefix + label + suffix; -}; + /** + * Similar to [`measure()`](#measure), but the resulting location will be + * relative to the supplied ancestor's location. + * + * Obtain a native node handle with `ReactNative.findNodeHandle(component)`. + */ -var beginMark = function(markName) { - performance.mark(formatMarkName(markName)); -}; + ReactNativeComponent.prototype.measureLayout = function measureLayout( + relativeToNativeNode, + onSuccess, + onFail /* currently unused */ + ) { + UIManager.measureLayout( + findNumericNodeHandleFiber(this), + relativeToNativeNode, + mountSafeCallback(this, onFail), + mountSafeCallback(this, onSuccess) + ); + }; -var clearMark = function(markName) { - performance.clearMarks(formatMarkName(markName)); -}; + /** + * This function sends props straight to native. They will not participate in + * future diff process - this means that if you do not include them in the + * next render, they will remain active (see [Direct + * Manipulation](docs/direct-manipulation.html)). + */ -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. + ReactNativeComponent.prototype.setNativeProps = function setNativeProps( + nativeProps + ) { + // Class components don't have viewConfig -> validateAttributes. + // Nor does it make sense to set native props on a non-native component. + // Instead, find the nearest host component and set props on it. + // Use findNodeHandle() rather than ReactNative.findNodeHandle() because + // We want the instance/wrapper (not the native tag). + var maybeInstance = void 0; - // Clear marks immediately to avoid growing buffer. - performance.clearMarks(formattedMarkName); - performance.clearMeasures(formattedLabel); -}; + // Fiber errors if findNodeHandle is called for an umounted component. + // Tests using ReactTestRenderer will trigger this case indirectly. + // Mimicking stack behavior, we should silently ignore this case. + // TODO Fix ReactTestRenderer so we can remove this try/catch. + try { + maybeInstance = findNodeHandle(this); + } catch (error) {} -var getFiberMarkName = function(label, debugID) { - return label + " (#" + debugID + ")"; -}; + // If there is no host component beneath this we should fail silently. + // This is not an error; it could mean a class component rendered null. + if (maybeInstance == null) { + return; + } -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 viewConfig = + maybeInstance.viewConfig || maybeInstance.canonical.viewConfig; -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); + var updatePayload = create(nativeProps, viewConfig.validAttributes); - 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); + // Avoid the overhead of bridge calls if there's no update. + // This is an expensive no-op for Android, and causes an unnecessary + // view invalidation for certain components (eg RCTTextInput) on iOS. + if (updatePayload != null) { + UIManager.updateView( + maybeInstance._nativeTag, + viewConfig.uiViewClassName, + updatePayload + ); + } + }; - var markName = getFiberMarkName(label, debugID); - beginMark(markName); - return true; -}; + return ReactNativeComponent; +})(React.Component); -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); -}; +// Don't change these two values: +var NoEffect = 0; +var PerformedWork = 1; -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); -}; +// You can change the rest (and add more). +var Placement = 2; +var Update = 4; +var PlacementAndUpdate = 6; +var Deletion = 8; +var ContentReset = 16; +var Callback = 32; +var Err = 64; +var Ref = 128; -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 MOUNTING = 1; +var MOUNTED = 2; +var UNMOUNTED = 3; -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); +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; } - 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; + while (node["return"]) { + node = node["return"]; + if ((node.effectTag & Placement) !== NoEffect) { + return MOUNTING; + } } - if ( - currentPhase !== null && - currentPhase !== "componentWillMount" && - currentPhase !== "componentWillReceiveProps" - ) { - hasScheduledUpdateInCurrentPhase = true; + } else { + while (node["return"]) { + node = node["return"]; } } -} - -function startRequestCallbackTimer() { - if (enableUserTimingAPI) { - if (supportsUserTiming && !isWaitingForCallback) { - isWaitingForCallback = true; - beginMark("(Waiting for async callback...)"); - } + 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 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 isFiberMounted(fiber) { + return isFiberMountedImpl(fiber) === MOUNTED; } -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; +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; } - 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); + var fiber = get(component); + if (!fiber) { + return false; } + return isFiberMountedImpl(fiber) === MOUNTED; } -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 assertIsMounted(fiber) { + invariant( + isFiberMountedImpl(fiber) === MOUNTED, + "Unable to find node on an unmounted component." + ); } -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; +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); + invariant( + state !== UNMOUNTED, + "Unable to find node on an unmounted component." + ); + if (state === MOUNTING) { + return null; } - fiber._debugIsCurrentlyTiming = false; - var warning$$1 = "An error was thrown inside this error boundary"; - endFiberMark(fiber, null, warning$$1); + return fiber; } -} - -function startPhaseTimer(fiber, phase) { - if (enableUserTimingAPI) { - if (!supportsUserTiming) { - return; - } - clearPendingPhaseMeasurement(); - if (!beginFiberMark(fiber, phase)) { - return; + // 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; } - 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); + // 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."); } - currentPhase = null; - currentPhaseFiber = null; - } -} -function startWorkLoopTimer(nextUnitOfWork) { - if (enableUserTimingAPI) { - currentFiber = nextUnitOfWork; - if (!supportsUserTiming) { - return; + 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; + } + invariant( + didFindChild, + "Child was not found in either parent set. This indicates a bug " + + "in React related to the return pointer. Please file an issue." + ); + } } - 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(); + + invariant( + a.alternate === b, + "Return fibers should always be each others' alternates. " + + "This error is likely caused by a bug in React. Please file an issue." + ); + } + // If the root is not a host container, we're in a disconnected tree. I.e. + // unmounted. + invariant( + a.tag === HostRoot, + "Unable to find node on an unmounted component." + ); + 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 stopWorkLoopTimer(interruptedBy) { - if (enableUserTimingAPI) { - if (!supportsUserTiming) { - return; +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; } - 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"; + if (node === currentParent) { + return null; + } + while (!node.sibling) { + if (!node["return"] || node["return"] === currentParent) { + return null; } - } else if (commitCountInCurrentWorkLoop > 1) { - warning$$1 = "There were cascading updates"; + node = node["return"]; } - commitCountInCurrentWorkLoop = 0; - // Pause any measurements until the next loop. - pauseTimers(); - endMark( - "(React Tree Reconciliation)", - "(React Tree Reconciliation)", - warning$$1 - ); + 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 startCommitTimer() { - if (enableUserTimingAPI) { - if (!supportsUserTiming) { - return; +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; } - isCommitting = true; - hasScheduledUpdateInCurrentCommit = false; - labelsInCurrentCommit.clear(); - beginMark("(Committing Changes)"); + 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 stopCommitTimer() { - if (enableUserTimingAPI) { - if (!supportsUserTiming) { - return; - } +var valueStack = []; - 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(); +var fiberStack = void 0; - endMark("(Committing Changes)", "(Committing Changes)", warning$$1); - } +{ + fiberStack = []; } -function startCommitHostEffectsTimer() { - if (enableUserTimingAPI) { - if (!supportsUserTiming) { - return; - } - effectCountInCurrentCommit = 0; - beginMark("(Committing Host Effects)"); - } +var index = -1; + +function createCursor(defaultValue) { + return { + current: defaultValue + }; } -function stopCommitHostEffectsTimer() { - if (enableUserTimingAPI) { - if (!supportsUserTiming) { - return; +function pop(cursor, fiber) { + if (index < 0) { + { + warning(false, "Unexpected pop."); } - var count = effectCountInCurrentCommit; - effectCountInCurrentCommit = 0; - endMark( - "(Committing Host Effects: " + count + " Total)", - "(Committing Host Effects)", - null - ); + return; } -} -function startCommitLifeCyclesTimer() { - if (enableUserTimingAPI) { - if (!supportsUserTiming) { - return; + { + if (fiber !== fiberStack[index]) { + warning(false, "Unexpected Fiber popped."); } - 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 - ); + cursor.current = valueStack[index]; + + valueStack[index] = null; + + { + fiberStack[index] = null; } -} -{ - var warnedAboutMissingGetChildContext = {}; + index--; } -// 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 push(cursor, value, fiber) { + index++; -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; + valueStack[index] = cursor.current; + + { + fiberStack[index] = fiber; } - return contextStackCursor.current; -} -function cacheContext(workInProgress, unmaskedContext, maskedContext) { - var instance = workInProgress.stateNode; - instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext; - instance.__reactInternalMemoizedMaskedChildContext = maskedContext; + cursor.current = value; } -function getMaskedContext(workInProgress, unmaskedContext) { - var type = workInProgress.type; - var contextTypes = type.contextTypes; - if (!contextTypes) { - return emptyObject; - } +function reset() { + while (index > -1) { + valueStack[index] = null; - // 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; - } + { + fiberStack[index] = null; + } - var context = {}; - for (var key in contextTypes) { - context[key] = unmaskedContext[key]; + index--; } +} - { - var name = getComponentName(workInProgress) || "Unknown"; - checkPropTypes( - contextTypes, - context, - "context", - name, - ReactDebugCurrentFiber.getCurrentFiberStackAddendum - ); - } +var describeComponentFrame = function(name, source, ownerName) { + return ( + "\n in " + + (name || "Unknown") + + (source + ? " (at " + + source.fileName.replace(/^.*[\\\/]/, "") + + ":" + + source.lineNumber + + ")" + : ownerName ? " (created by " + ownerName + ")" : "") + ); +}; - // 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); +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 ""; } - - return context; -} - -function hasContextChanged() { - return didPerformWorkStackCursor.current; } -function isContextConsumer(fiber) { - return fiber.tag === ClassComponent && fiber.type.contextTypes != null; +// 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 isContextProvider(fiber) { - return fiber.tag === ClassComponent && fiber.type.childContextTypes != null; +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 popContextProvider(fiber) { - if (!isContextProvider(fiber)) { - return; +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); } - - pop(didPerformWorkStackCursor, fiber); - pop(contextStackCursor, fiber); + return null; } -function popTopLevelContextObject(fiber) { - pop(didPerformWorkStackCursor, fiber); - pop(contextStackCursor, fiber); +function resetCurrentFiber() { + ReactDebugCurrentFrame.getCurrentStack = null; + ReactDebugCurrentFiber.current = null; + ReactDebugCurrentFiber.phase = null; } -function pushTopLevelContextObject(fiber, context, didChange) { - invariant( - contextStackCursor.cursor == null, - "Unexpected context found on stack. " + - "This error is likely caused by a bug in React. Please file an issue." - ); - - push(contextStackCursor, context, fiber); - push(didPerformWorkStackCursor, didChange, fiber); +function setCurrentFiber(fiber) { + ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackAddendum; + ReactDebugCurrentFiber.current = fiber; + ReactDebugCurrentFiber.phase = null; } -function processChildContext(fiber, parentContext) { - var instance = fiber.stateNode; - var childContextTypes = fiber.type.childContextTypes; +function setCurrentPhase(phase) { + ReactDebugCurrentFiber.phase = phase; +} - // 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"; +var ReactDebugCurrentFiber = { + current: null, + phase: null, + resetCurrentFiber: resetCurrentFiber, + setCurrentFiber: setCurrentFiber, + setCurrentPhase: setCurrentPhase, + getCurrentFiberOwnerName: getCurrentFiberOwnerName, + getCurrentFiberStackAddendum: getCurrentFiberStackAddendum +}; - 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; - } +// Re-export dynamic flags from the fbsource version. +var _require = require("ReactFeatureFlags"); - var childContext = void 0; - { - ReactDebugCurrentFiber.setCurrentPhase("getChildContext"); - } - startPhaseTimer(fiber, "getChildContext"); - childContext = instance.getChildContext(); - stopPhaseTimer(); - { - ReactDebugCurrentFiber.setCurrentPhase(null); - } - for (var contextKey in childContext) { - invariant( - contextKey in childContextTypes, - '%s.getChildContext(): key "%s" is not defined in childContextTypes.', - getComponentName(fiber) || "Unknown", - contextKey - ); - } - { - 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 - ); - } +var debugRenderPhaseSideEffects = _require.debugRenderPhaseSideEffects; +var debugRenderPhaseSideEffectsForStrictMode = + _require.debugRenderPhaseSideEffectsForStrictMode; +var warnAboutDeprecatedLifecycles = _require.warnAboutDeprecatedLifecycles; - return Object.assign({}, parentContext, childContext); -} +var enableAsyncSubtreeAPI = true; -function pushContextProvider(workInProgress) { - if (!isContextProvider(workInProgress)) { - return false; - } +var enableUserTimingAPI = true; +var enableMutatingReconciler = true; +var enableNoopReconciler = false; +var enablePersistentReconciler = false; +var enableNewContextAPI = 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; +// Only used in www builds. - // 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 - ); +// 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"; - return true; -} +// 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(); -function invalidateContextProvider(workInProgress, didChange) { - var instance = workInProgress.stateNode; - invariant( - instance, - "Expected to have an instance by this point. " + - "This error is likely caused by a bug in React. Please file an issue." - ); +var formatMarkName = function(markName) { + return reactEmoji + " " + markName; +}; - 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; +var formatLabel = function(label, warning$$1) { + var prefix = warning$$1 ? warningEmoji + " " : reactEmoji + " "; + var suffix = warning$$1 ? " Warning: " + warning$$1 : ""; + return "" + prefix + label + suffix; +}; - // 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); - } -} +var beginMark = function(markName) { + performance.mark(formatMarkName(markName)); +}; -function resetContext() { - previousContext = emptyObject; - contextStackCursor.current = emptyObject; - didPerformWorkStackCursor.current = false; -} +var clearMark = function(markName) { + performance.clearMarks(formatMarkName(markName)); +}; -function findCurrentUnmaskedContext(fiber) { - // Currently this is only used with renderSubtreeIntoContainer; not sure if it - // makes sense elsewhere - invariant( - isFiberMounted(fiber) && fiber.tag === ClassComponent, - "Expected subtree parent to be a mounted class component. " + - "This error is likely caused by a bug in React. Please file an issue." - ); +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. - var node = fiber; - while (node.tag !== HostRoot) { - if (isContextProvider(node)) { - return node.stateNode.__reactInternalMemoizedMergedChildContext; - } - var parent = node["return"]; - invariant( - parent, - "Found unexpected detached subtree parent. " + - "This error is likely caused by a bug in React. Please file an issue." - ); - node = parent; + // 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; } - return node.stateNode.context; -} +}; -var NoWork = 0; // TODO: Use an opaque type once ESLint et al support the syntax +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); -var Sync = 1; -var Never = 2147483647; // Max int32: Math.pow(2, 31) - 1 + 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 UNIT_SIZE = 10; -var MAGIC_NUMBER_OFFSET = 2; + var markName = getFiberMarkName(label, debugID); + beginMark(markName); + return true; +}; -// 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; -} +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); +}; -function expirationTimeToMs(expirationTime) { - return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE; -} +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); +}; -function ceiling(num, precision) { - return (((num / precision) | 0) + 1) * precision; -} +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 CallComponent: + case ReturnComponent: + case Fragment: + return true; + default: + return false; + } +}; -function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) { - return ceiling( - currentTime + expirationInMs / UNIT_SIZE, - bucketSizeMs / UNIT_SIZE - ); -} +var clearPendingPhaseMeasurement = function() { + if (currentPhase !== null && currentPhaseFiber !== null) { + clearFiberMark(currentPhaseFiber, currentPhase); + } + currentPhaseFiber = null; + currentPhase = null; + hasScheduledUpdateInCurrentPhase = false; +}; -var NoContext = 0; -var AsyncUpdates = 1; +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 hasBadMapPolyfill = false; - try { - var nonExtensibleObject = Object.preventExtensions({}); - /* eslint-disable no-new */ - new Map([[nonExtensibleObject, null]]); - new Set([nonExtensibleObject]); - /* eslint-enable no-new */ - } catch (e) { - // TODO: Consider warning about bad polyfills - hasBadMapPolyfill = true; +var resumeTimersRecursively = function(fiber) { + if (fiber["return"] !== null) { + resumeTimersRecursively(fiber["return"]); } -} + if (fiber._debugIsCurrentlyTiming) { + beginFiberMark(fiber, null); + } +}; -// A Fiber is work on a Component that needs to be done or was done. There can -// be more than one per component. +var resumeTimers = function() { + // Resumes all measurements that were active during the last deferred loop. + if (currentFiber !== null) { + resumeTimersRecursively(currentFiber); + } +}; -{ - var debugCounter = 1; +function recordEffect() { + if (enableUserTimingAPI) { + effectCountInCurrentCommit++; + } } -function FiberNode(tag, pendingProps, 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 = pendingProps; - 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); +function recordScheduleUpdate() { + if (enableUserTimingAPI) { + if (isCommitting) { + hasScheduledUpdateInCurrentCommit = true; + } + if ( + currentPhase !== null && + currentPhase !== "componentWillMount" && + currentPhase !== "componentWillReceiveProps" + ) { + hasScheduledUpdateInCurrentPhase = true; } } } -// 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, pendingProps, key, internalContextTag) { - // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors - return new FiberNode(tag, pendingProps, key, internalContextTag); -}; - -function shouldConstruct(Component) { - return !!(Component.prototype && Component.prototype.isReactComponent); +function startRequestCallbackTimer() { + if (enableUserTimingAPI) { + if (supportsUserTiming && !isWaitingForCallback) { + isWaitingForCallback = true; + beginMark("(Waiting for async callback...)"); + } + } } -// 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, - pendingProps, - 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; +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 + ); } - - workInProgress.alternate = current; - current.alternate = workInProgress; - } else { - workInProgress.pendingProps = pendingProps; - - // 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.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 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 createFiberFromElement(element, internalContextTag, expirationTime) { - var owner = null; - { - owner = element._owner; +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); } +} - var fiber = void 0; - var type = element.type; - var key = element.key; - var pendingProps = element.props; - if (typeof type === "function") { - fiber = shouldConstruct(type) - ? createFiber(ClassComponent, pendingProps, key, internalContextTag) - : createFiber( - IndeterminateComponent, - pendingProps, - key, - internalContextTag - ); - fiber.type = type; - } else if (typeof type === "string") { - fiber = createFiber(HostComponent, pendingProps, key, internalContextTag); - fiber.type = type; - } 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 = pendingProps; - } 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 + "`."; - } +function stopWorkTimer(fiber) { + if (enableUserTimingAPI) { + if (!supportsUserTiming || shouldIgnoreFiber(fiber)) { + return; } - 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 - ); + // 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); } +} - { - fiber._debugSource = element._source; - fiber._debugOwner = element._owner; +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); } +} - fiber.expirationTime = expirationTime; - - return fiber; +function startPhaseTimer(fiber, phase) { + if (enableUserTimingAPI) { + if (!supportsUserTiming) { + return; + } + clearPendingPhaseMeasurement(); + if (!beginFiberMark(fiber, phase)) { + return; + } + currentPhaseFiber = fiber; + currentPhase = phase; + } } -function createFiberFromFragment( - elements, - internalContextTag, - expirationTime, - key -) { - var fiber = createFiber(Fragment, elements, key, internalContextTag); - fiber.expirationTime = expirationTime; - return fiber; +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 createFiberFromText(content, internalContextTag, expirationTime) { - var fiber = createFiber(HostText, content, null, internalContextTag); - fiber.expirationTime = expirationTime; - return fiber; +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 createFiberFromHostInstanceForDeletion() { - var fiber = createFiber(HostComponent, null, null, NoContext); - fiber.type = "DELETED"; - return fiber; -} - -function createFiberFromCall(call, internalContextTag, expirationTime) { - var fiber = createFiber(CallComponent, call, call.key, internalContextTag); - fiber.type = call.handler; - fiber.expirationTime = expirationTime; - return fiber; +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 createFiberFromReturn(returnNode, internalContextTag, expirationTime) { - var fiber = createFiber(ReturnComponent, null, null, internalContextTag); - fiber.expirationTime = expirationTime; - return fiber; +function startCommitTimer() { + if (enableUserTimingAPI) { + if (!supportsUserTiming) { + return; + } + isCommitting = true; + hasScheduledUpdateInCurrentCommit = false; + labelsInCurrentCommit.clear(); + beginMark("(Committing Changes)"); + } } -function createFiberFromPortal(portal, internalContextTag, expirationTime) { - var pendingProps = portal.children !== null ? portal.children : []; - var fiber = createFiber( - HostPortal, - pendingProps, - portal.key, - internalContextTag - ); - fiber.expirationTime = expirationTime; - fiber.stateNode = { - containerInfo: portal.containerInfo, - pendingChildren: null, // Used by persistent updates - implementation: portal.implementation - }; - return fiber; -} +function stopCommitTimer() { + if (enableUserTimingAPI) { + if (!supportsUserTiming) { + return; + } -// TODO: This should be lifted into the renderer. + 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(); -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, - firstBatch: null, - nextScheduledRoot: null - }; - uninitializedFiber.stateNode = root; - return root; + endMark("(Committing Changes)", "(Committing Changes)", warning$$1); + } } -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 startCommitHostEffectsTimer() { + if (enableUserTimingAPI) { + if (!supportsUserTiming) { + return; } - }; + effectCountInCurrentCommit = 0; + beginMark("(Committing Host Effects)"); + } } -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); +function stopCommitHostEffectsTimer() { + if (enableUserTimingAPI) { + if (!supportsUserTiming) { + return; } + var count = effectCountInCurrentCommit; + effectCountInCurrentCommit = 0; + endMark( + "(Committing Host Effects: " + count + " Total)", + "(Committing Host Effects)", + null + ); } - // DevTools exists - return true; } -function onCommitRoot(root) { - if (typeof onCommitFiberRoot === "function") { - onCommitFiberRoot(root); +function startCommitLifeCyclesTimer() { + if (enableUserTimingAPI) { + if (!supportsUserTiming) { + return; + } + effectCountInCurrentCommit = 0; + beginMark("(Calling Lifecycle Methods)"); } } -function onCommitUnmount(fiber) { - if (typeof onCommitFiberUnmount === "function") { - onCommitFiberUnmount(fiber); +function stopCommitLifeCyclesTimer() { + if (enableUserTimingAPI) { + if (!supportsUserTiming) { + return; + } + var count = effectCountInCurrentCommit; + effectCountInCurrentCommit = 0; + endMark( + "(Calling Lifecycle Methods: " + count + " Total)", + "(Calling Lifecycle Methods)", + null + ); } } +var warnedAboutMissingGetChildContext = void 0; + { - var didWarnUpdateInsideUpdate = false; + warnedAboutMissingGetChildContext = {}; } -// 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. +// 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 createUpdateQueue(baseState) { - var queue = { - baseState: baseState, - expirationTime: NoWork, - first: null, - last: null, - callbackList: null, - hasForceUpdate: false, - isInitialized: false - }; - { - queue.isProcessing = false; +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 queue; + return contextStackCursor.current; } -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 cacheContext(workInProgress, unmaskedContext, maskedContext) { + var instance = workInProgress.stateNode; + instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext; + instance.__reactInternalMemoizedMaskedChildContext = maskedContext; } -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); +function getMaskedContext(workInProgress, unmaskedContext) { + var type = workInProgress.type; + var contextTypes = type.contextTypes; + if (!contextTypes) { + return emptyObject; } - var queue2 = void 0; - if (alternateFiber !== null) { - queue2 = alternateFiber.updateQueue; - if (queue2 === null) { - queue2 = alternateFiber.updateQueue = createUpdateQueue(null); - } - } else { - queue2 = null; + // 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; } - 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; - } + var context = {}; + for (var key in contextTypes) { + context[key] = unmaskedContext[key]; } - // If there's only one queue, add the update to that queue and exit. - if (queue2 === null) { - insertUpdateIntoQueue(queue1, update); - return; + { + var name = getComponentName(workInProgress) || "Unknown"; + checkPropTypes( + contextTypes, + context, + "context", + name, + ReactDebugCurrentFiber.getCurrentFiberStackAddendum + ); } - // 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; + // 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); } - // 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; + return context; } -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 hasContextChanged() { + return didPerformWorkStackCursor.current; } -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); - } +function isContextConsumer(fiber) { + return fiber.tag === ClassComponent && fiber.type.contextTypes != null; +} - return updateFn.call(instance, prevState, props); - } else { - return partialState; - } +function isContextProvider(fiber) { + return fiber.tag === ClassComponent && fiber.type.childContextTypes != null; } -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 - }; +function popContextProvider(fiber) { + if (!isContextProvider(fiber)) { + return; } - { - // Set this flag so we can warn if setState is called inside the update - // function of another setState. - queue.isProcessing = true; - } + pop(didPerformWorkStackCursor, fiber); + pop(contextStackCursor, fiber); +} - // Reset the remaining expiration time. If we skip over any updates, we'll - // increase this accordingly. - queue.expirationTime = NoWork; +function popTopLevelContextObject(fiber) { + pop(didPerformWorkStackCursor, fiber); + pop(contextStackCursor, fiber); +} - // 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; - } +function pushTopLevelContextObject(fiber, context, didChange) { + invariant( + contextStackCursor.cursor == null, + "Unexpected context found on stack. " + + "This error is likely caused by a bug in React. Please file an issue." + ); - // This update does have sufficient priority. + push(contextStackCursor, context, fiber); + push(didPerformWorkStackCursor, didChange, fiber); +} - // 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; - } - } +function processChildContext(fiber, parentContext) { + var instance = fiber.stateNode; + var childContextTypes = fiber.type.childContextTypes; - // 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 = Object.assign({}, state, _partialState); - } else { - state = Object.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 = []; + // 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 + ); } - _callbackList.push(update); } - update = update.next; + return parentContext; } - 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; + var childContext = void 0; + { + ReactDebugCurrentFiber.setCurrentPhase("getChildContext"); } - - if (!didSkip) { - didSkip = true; - queue.baseState = state; + startPhaseTimer(fiber, "getChildContext"); + childContext = instance.getChildContext(); + stopPhaseTimer(); + { + ReactDebugCurrentFiber.setCurrentPhase(null); + } + for (var contextKey in childContext) { + invariant( + contextKey in childContextTypes, + '%s.getChildContext(): key "%s" is not defined in childContextTypes.', + getComponentName(fiber) || "Unknown", + contextKey + ); } - { - // No longer processing. - queue.isProcessing = false; + 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 state; + return Object.assign({}, parentContext, childContext); } -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; - invariant( - typeof _callback === "function", - "Invalid argument passed as callback. Expected a function. Instead " + - "received: %s", - _callback - ); - _callback.call(context); +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; } -var fakeInternalInstance = {}; -var isArray = Array.isArray; +function invalidateContextProvider(workInProgress, didChange) { + var instance = workInProgress.stateNode; + invariant( + instance, + "Expected to have an instance by this point. " + + "This error is likely caused by a bug in React. Please file an issue." + ); -{ - var didWarnAboutStateAssignmentForComponent = {}; + 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; - 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 - ); - }; + // 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); + } +} - // 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); +function resetContext() { + previousContext = emptyObject; + contextStackCursor.current = emptyObject; + didPerformWorkStackCursor.current = false; } -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 findCurrentUnmaskedContext(fiber) { + // Currently this is only used with renderSubtreeIntoContainer; not sure if it + // makes sense elsewhere + invariant( + isFiberMounted(fiber) && fiber.tag === ClassComponent, + "Expected subtree parent to be a mounted class component. " + + "This error is likely caused by a bug in React. Please file an issue." + ); - 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 node = fiber; + while (node.tag !== HostRoot) { + if (isContextProvider(node)) { + return node.stateNode.__reactInternalMemoizedMergedChildContext; } + var parent = node["return"]; + invariant( + parent, + "Found unexpected detached subtree parent. " + + "This error is likely caused by a bug in React. Please file an issue." + ); + node = parent; + } + return node.stateNode.context; +} - 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); - } +// Max 31 bit integer. The max integer size in V8 for 32-bit systems. +// Math.pow(2, 30) - 1 +// 0b111111111111111111111111111111 +var MAX_SIGNED_31_BIT_INT = 1073741823; - { - warning( - shouldUpdate !== undefined, - "%s.shouldComponentUpdate(): Returned undefined instead of a " + - "boolean value. Make sure to return true or false.", - getComponentName(workInProgress) || "Unknown" - ); - } +// TODO: Use an opaque type once ESLint et al support the syntax - return shouldUpdate; - } +var NoWork = 0; +var Sync = 1; +var Never = MAX_SIGNED_31_BIT_INT; - if (type.prototype && type.prototype.isPureReactComponent) { - return ( - !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) - ); - } +var UNIT_SIZE = 10; +var MAGIC_NUMBER_OFFSET = 2; - return true; - } +// 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 checkClassInstance(workInProgress) { - var instance = workInProgress.stateNode; - var type = workInProgress.type; - { - var name = getComponentName(workInProgress); - var renderPresent = instance.render; +function expirationTimeToMs(expirationTime) { + return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE; +} - 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 - ); - } - } +function ceiling(num, precision) { + return (((num / precision) | 0) + 1) * precision; +} - 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 - ); - } +function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) { + return ceiling( + currentTime + expirationInMs / UNIT_SIZE, + bucketSizeMs / UNIT_SIZE + ); +} - 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) - ); - } - } +var NoContext = 0; +var AsyncUpdates = 1; +var StrictMode = 2; - function resetInputPointers(workInProgress, instance) { - instance.props = workInProgress.memoizedProps; - instance.state = workInProgress.memoizedState; - } +var hasBadMapPolyfill = void 0; - 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; - } +{ + hasBadMapPolyfill = false; + try { + var nonExtensibleObject = Object.preventExtensions({}); + var testMap = new Map([[nonExtensibleObject, null]]); + var testSet = new Set([nonExtensibleObject]); + // This is necessary for Rollup to not consider these unused. + // https://github.com/rollup/rollup/issues/1771 + // TODO: we can remove these if Rollup fixes the bug. + testMap.set(0, 0); + testSet.add(0); + } catch (e) { + // TODO: Consider warning about bad polyfills + hasBadMapPolyfill = true; } +} - 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); +// A Fiber is work on a Component that needs to be done or was done. There can +// be more than one per component. - // 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); - } +var debugCounter = void 0; - return instance; - } +{ + debugCounter = 1; +} - function callComponentWillMount(workInProgress, instance) { - startPhaseTimer(workInProgress, "componentWillMount"); - var oldState = instance.state; - instance.componentWillMount(); - stopPhaseTimer(); +function FiberNode(tag, pendingProps, key, internalContextTag) { + // Instance + this.tag = tag; + this.key = key; + this.type = null; + this.stateNode = null; - // Simulate an async bailout/interruption by invoking lifecycle twice. - if (debugRenderPhaseSideEffects) { - instance.componentWillMount(); - } + // Fiber + this["return"] = null; + this.child = null; + this.sibling = null; + this.index = 0; - 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); - } - } + this.ref = null; - function callComponentWillReceiveProps( - workInProgress, - instance, - newProps, - newContext - ) { - startPhaseTimer(workInProgress, "componentWillReceiveProps"); - var oldState = instance.state; - instance.componentWillReceiveProps(newProps, newContext); - stopPhaseTimer(); + this.pendingProps = pendingProps; + this.memoizedProps = null; + this.updateQueue = null; + this.memoizedState = null; - // Simulate an async bailout/interruption by invoking lifecycle twice. - if (debugRenderPhaseSideEffects) { - instance.componentWillReceiveProps(newProps, newContext); - } + this.internalContextTag = internalContextTag; - 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); - } + // Effects + this.effectTag = NoEffect; + this.nextEffect = null; - var instance = workInProgress.stateNode; - var state = instance.state || null; - var props = workInProgress.pendingProps; - var unmaskedContext = getUnmaskedContext(workInProgress); + this.firstEffect = null; + this.lastEffect = null; - instance.props = props; - instance.state = workInProgress.memoizedState = state; - instance.refs = emptyObject; - instance.context = getMaskedContext(workInProgress, unmaskedContext); + this.expirationTime = NoWork; - if ( - enableAsyncSubtreeAPI && - workInProgress.type != null && - workInProgress.type.prototype != null && - workInProgress.type.prototype.unstable_isAsyncReactComponent === true - ) { - workInProgress.internalContextTag |= AsyncUpdates; - } + this.alternate = null; - 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; + { + this._debugID = debugCounter++; + this._debugSource = null; + this._debugOwner = null; + this._debugIsCurrentlyTiming = false; + if (!hasBadMapPolyfill && typeof Object.preventExtensions === "function") { + Object.preventExtensions(this); } } +} - // 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); +// 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, pendingProps, key, internalContextTag) { + // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors + return new FiberNode(tag, pendingProps, key, internalContextTag); +}; - // const oldContext = instance.context; - // const oldProps = workInProgress.memoizedProps; +function shouldConstruct(Component) { + return !!(Component.prototype && Component.prototype.isReactComponent); +} - // if ( - // typeof instance.componentWillReceiveProps === 'function' && - // (oldProps !== newProps || oldContext !== newContext) - // ) { - // callComponentWillReceiveProps( - // workInProgress, - // instance, - // newProps, - // newContext, - // ); - // } +// 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, + pendingProps, + current.key, + current.internalContextTag + ); + workInProgress.type = current.type; + workInProgress.stateNode = current.stateNode; - // // Process the update queue before calling shouldComponentUpdate - // const updateQueue = workInProgress.updateQueue; - // if (updateQueue !== null) { - // newState = processUpdateQueue( - // workInProgress, - // updateQueue, - // instance, - // newState, - // newProps, - // priorityLevel, - // ); - // } + { + // DEV-only fields + workInProgress._debugID = current._debugID; + workInProgress._debugSource = current._debugSource; + workInProgress._debugOwner = current._debugOwner; + } - // // TODO: Should we deal with a setState that happened after the last - // // componentWillMount and before this componentWillMount? Probably - // // unsupported anyway. + workInProgress.alternate = current; + current.alternate = workInProgress; + } else { + workInProgress.pendingProps = pendingProps; - // 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; - // } + // We already have an alternate. + // Reset the effect tag. + workInProgress.effectTag = NoEffect; - // // Update the input pointers now so that they are correct when we call - // // componentWillMount - // instance.props = newProps; - // instance.state = newState; - // instance.context = newContext; + // The effect list is no longer valid. + workInProgress.nextEffect = null; + workInProgress.firstEffect = null; + workInProgress.lastEffect = null; + } - // 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, - // ); - // } - // } + workInProgress.expirationTime = expirationTime; - // if (typeof instance.componentDidMount === 'function') { - // workInProgress.effectTag |= Update; - // } + workInProgress.child = current.child; + workInProgress.memoizedProps = current.memoizedProps; + workInProgress.memoizedState = current.memoizedState; + workInProgress.updateQueue = current.updateQueue; - // instance.state = newState; + // These will be overridden during the parent's reconciliation + workInProgress.sibling = current.sibling; + workInProgress.index = current.index; + workInProgress.ref = current.ref; - // return true; - // } + return workInProgress; +} - // 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); +function createHostRootFiber(isAsync) { + var internalContextTag = isAsync ? AsyncUpdates | StrictMode : NoContext; + return createFiber(HostRoot, null, null, internalContextTag); +} - var oldProps = workInProgress.memoizedProps; - var newProps = workInProgress.pendingProps; - var oldContext = instance.context; - var newUnmaskedContext = getUnmaskedContext(workInProgress); - var newContext = getMaskedContext(workInProgress, newUnmaskedContext); +function createFiberFromElement(element, internalContextTag, expirationTime) { + var owner = null; + { + owner = element._owner; + } - // 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. + var fiber = void 0; + var type = element.type; + var key = element.key; + var pendingProps = element.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; + var fiberTag = void 0; + if (typeof type === "function") { + fiberTag = shouldConstruct(type) ? ClassComponent : IndeterminateComponent; + } else if (typeof type === "string") { + fiberTag = HostComponent; + } else { + switch (type) { + case REACT_FRAGMENT_TYPE: + return createFiberFromFragment( + pendingProps.children, + internalContextTag, + expirationTime, + key + ); + case REACT_STRICT_MODE_TYPE: + fiberTag = Mode; + internalContextTag |= StrictMode; + break; + case REACT_CALL_TYPE: + fiberTag = CallComponent; + break; + case REACT_RETURN_TYPE: + fiberTag = ReturnComponent; + break; + default: { + if (typeof type === "object" && type !== null) { + switch (type.$$typeof) { + case REACT_PROVIDER_TYPE: + fiberTag = ContextProvider; + break; + case REACT_CONTEXT_TYPE: + // This is a consumer + fiberTag = ContextConsumer; + break; + default: + if (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 = pendingProps; + fiber.expirationTime = expirationTime; + return fiber; + } else { + throwOnInvalidElementType(type, owner); + } + break; + } + } else { + throwOnInvalidElementType(type, owner); } } - return false; } + } - var shouldUpdate = checkShouldComponentUpdate( - workInProgress, - oldProps, - newProps, - oldState, - newState, - newContext - ); + fiber = createFiber(fiberTag, pendingProps, key, internalContextTag); + fiber.type = type; + fiber.expirationTime = expirationTime; - if (shouldUpdate) { - if (typeof instance.componentWillUpdate === "function") { - startPhaseTimer(workInProgress, "componentWillUpdate"); - instance.componentWillUpdate(newProps, newState, newContext); - stopPhaseTimer(); + { + fiber._debugSource = element._source; + fiber._debugOwner = element._owner; + } - // 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; - } - } + return fiber; +} - // 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); +function throwOnInvalidElementType(type, owner) { + 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 + "`."; } - - // 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; } + 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 + ); +} - return { - adoptClassInstance: adoptClassInstance, - constructClassInstance: constructClassInstance, - mountClassInstance: mountClassInstance, - // resumeMountClassInstance, - updateClassInstance: updateClassInstance - }; -}; +function createFiberFromFragment( + elements, + internalContextTag, + expirationTime, + key +) { + var fiber = createFiber(Fragment, elements, key, internalContextTag); + fiber.expirationTime = expirationTime; + return fiber; +} -var getCurrentFiberStackAddendum$1 = - ReactDebugCurrentFiber.getCurrentFiberStackAddendum; +function createFiberFromText(content, internalContextTag, expirationTime) { + var fiber = createFiber(HostText, content, null, internalContextTag); + fiber.expirationTime = expirationTime; + return fiber; +} -{ - 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 = {}; +function createFiberFromHostInstanceForDeletion() { + var fiber = createFiber(HostComponent, null, null, NoContext); + fiber.type = "DELETED"; + return fiber; +} - var warnForMissingKey = function(child) { - if (child === null || typeof child !== "object") { - return; - } - if (!child._store || child._store.validated || child.key != null) { - return; - } - invariant( - typeof child._store === "object", - "React Component in warnForMissingKey should have a _store. " + - "This error is likely caused by a bug in React. Please file an issue." - ); - child._store.validated = true; +function createFiberFromPortal(portal, internalContextTag, expirationTime) { + var pendingProps = portal.children !== null ? portal.children : []; + var fiber = createFiber( + HostPortal, + pendingProps, + portal.key, + internalContextTag + ); + fiber.expirationTime = expirationTime; + fiber.stateNode = { + containerInfo: portal.containerInfo, + pendingChildren: null, // Used by persistent updates + implementation: portal.implementation + }; + return fiber; +} - 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; +// TODO: This should be lifted into the renderer. - 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() - ); +function createFiberRoot(containerInfo, isAsync, hydrate) { + // Cyclic construction. This cheats the type system right now because + // stateNode is any. + var uninitializedFiber = createHostRootFiber(isAsync); + var root = { + current: uninitializedFiber, + containerInfo: containerInfo, + pendingChildren: null, + remainingExpirationTime: NoWork, + isReadyForCommit: false, + finishedWork: null, + context: null, + pendingContext: null, + hydrate: hydrate, + firstBatch: null, + nextScheduledRoot: null }; + uninitializedFiber.stateNode = root; + return root; } -var isArray$1 = Array.isArray; +var onCommitFiberRoot = null; +var onCommitFiberUnmount = null; +var hasLoggedError = false; -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; - invariant( - ownerFiber.tag === ClassComponent, - "Stateless function components cannot have refs." - ); - inst = ownerFiber.stateNode; +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); } - invariant( - inst, - "Missing owner for string ref %s. This error is likely caused by a " + - "bug in React. Please file an issue.", - mixedRef - ); - 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 { - invariant( - typeof mixedRef === "string", - "Expected ref to be a function or a string." - ); - invariant( - element._owner, - "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 + } + }; +} + +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; } - return mixedRef; -} - -function throwOnInvalidObjectType(returnFiber, newChild) { - if (returnFiber.type !== "textarea") { - var addendum = ""; + 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. { - addendum = - " If you meant to render a collection of children, use an array " + - "instead." + - (getCurrentFiberStackAddendum$1() || ""); + warning(false, "React DevTools encountered an error: %s.", err); } - 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 - ); } + // DevTools exists + return true; } -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; +function onCommitRoot(root) { + if (typeof onCommitFiberRoot === "function") { + onCommitFiberRoot(root); } - 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 onCommitUnmount(fiber) { + if (typeof onCommitFiberUnmount === "function") { + onCommitFiberUnmount(fiber); } +} - function deleteRemainingChildren(returnFiber, currentFirstChild) { - if (!shouldTrackSideEffects) { - // Noop. - return null; - } +var ReactStrictModeWarnings = { + discardPendingWarnings: function() {}, + flushPendingDeprecationWarnings: function() {}, + flushPendingUnsafeLifecycleWarnings: function() {}, + recordDeprecationWarnings: function(fiber, instance) {}, + recordUnsafeLifecycleWarnings: function(fiber, instance) {} +}; - // 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; - } +{ + var LIFECYCLE_SUGGESTIONS = { + UNSAFE_componentWillMount: "componentDidMount", + UNSAFE_componentWillReceiveProps: "static getDerivedStateFromProps", + UNSAFE_componentWillUpdate: "componentDidUpdate" + }; - 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 pendingComponentWillMountWarnings = []; + var pendingComponentWillReceivePropsWarnings = []; + var pendingComponentWillUpdateWarnings = []; + var pendingUnsafeLifecycleWarnings = new Map(); - var existingChild = currentFirstChild; - while (existingChild !== null) { - if (existingChild.key !== null) { - existingChildren.set(existingChild.key, existingChild); - } else { - existingChildren.set(existingChild.index, existingChild); + // Tracks components we have already warned about. + var didWarnAboutDeprecatedLifecycles = new Set(); + var didWarnAboutUnsafeLifecycles = new Set(); + + ReactStrictModeWarnings.discardPendingWarnings = function() { + pendingComponentWillMountWarnings = []; + pendingComponentWillReceivePropsWarnings = []; + pendingComponentWillUpdateWarnings = []; + pendingUnsafeLifecycleWarnings = new Map(); + }; + + ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function() { + pendingUnsafeLifecycleWarnings.forEach(function( + lifecycleWarningsMap, + strictRoot + ) { + var lifecyclesWarningMesages = []; + + Object.keys(lifecycleWarningsMap).forEach(function(lifecycle) { + var lifecycleWarnings = lifecycleWarningsMap[lifecycle]; + if (lifecycleWarnings.length > 0) { + var componentNames = new Set(); + lifecycleWarnings.forEach(function(fiber) { + componentNames.add(getComponentName(fiber) || "Component"); + didWarnAboutUnsafeLifecycles.add(fiber.type); + }); + + var formatted = lifecycle.replace("UNSAFE_", ""); + var suggestion = LIFECYCLE_SUGGESTIONS[lifecycle]; + var sortedComponentNames = Array.from(componentNames) + .sort() + .join(", "); + + lifecyclesWarningMesages.push( + formatted + + ": Please update the following components to use " + + (suggestion + " instead: " + sortedComponentNames) + ); + } + }); + + if (lifecyclesWarningMesages.length > 0) { + var strictRootComponentStack = getStackAddendumByWorkInProgressFiber( + strictRoot + ); + + warning( + false, + "Unsafe lifecycle methods were found within a strict-mode tree:%s" + + "\n\n%s" + + "\n\nLearn more about this warning here:" + + "\nhttps://fb.me/react-strict-mode-warnings", + strictRootComponentStack, + lifecyclesWarningMesages.join("\n\n") + ); } - 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; - } + pendingUnsafeLifecycleWarnings = new Map(); + }; - 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; + var getStrictRoot = function(fiber) { + var maybeStrictRoot = null; + + while (fiber !== null) { + if (fiber.internalContextTag & StrictMode) { + maybeStrictRoot = fiber; } - } 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; + fiber = fiber["return"]; } - 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; - } - } + return maybeStrictRoot; + }; - 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 + ReactStrictModeWarnings.flushPendingDeprecationWarnings = function() { + if (pendingComponentWillMountWarnings.length > 0) { + var uniqueNames = new Set(); + pendingComponentWillMountWarnings.forEach(function(fiber) { + uniqueNames.add(getComponentName(fiber) || "Component"); + didWarnAboutDeprecatedLifecycles.add(fiber.type); + }); + + var sortedNames = Array.from(uniqueNames) + .sort() + .join(", "); + + warning( + false, + "componentWillMount is deprecated and will be removed in the next major version. " + + "Use componentDidMount instead. As a temporary workaround, " + + "you can rename to UNSAFE_componentWillMount." + + "\n\nPlease update the following components: %s" + + "\n\nLearn more about this warning here:" + + "\nhttps://fb.me/react-async-component-lifecycle-hooks", + sortedNames ); - created.ref = coerceRef(current, element); - created["return"] = returnFiber; - return created; + + pendingComponentWillMountWarnings = []; } - } - 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 + if (pendingComponentWillReceivePropsWarnings.length > 0) { + var _uniqueNames = new Set(); + pendingComponentWillReceivePropsWarnings.forEach(function(fiber) { + _uniqueNames.add(getComponentName(fiber) || "Component"); + didWarnAboutDeprecatedLifecycles.add(fiber.type); + }); + + var _sortedNames = Array.from(_uniqueNames) + .sort() + .join(", "); + + warning( + false, + "componentWillReceiveProps is deprecated and will be removed in the next major version. " + + "Use static getDerivedStateFromProps instead." + + "\n\nPlease update the following components: %s" + + "\n\nLearn more about this warning here:" + + "\nhttps://fb.me/react-async-component-lifecycle-hooks", + _sortedNames ); - created["return"] = returnFiber; - return created; - } else { - // Move based on index - var existing = useFiber(current, call, expirationTime); - existing["return"] = returnFiber; - return existing; + + pendingComponentWillReceivePropsWarnings = []; } - } - function updateReturn(returnFiber, current, returnNode, expirationTime) { - if (current === null || current.tag !== ReturnComponent) { - // Insert - var created = createFiberFromReturn( - returnNode, - returnFiber.internalContextTag, - expirationTime + if (pendingComponentWillUpdateWarnings.length > 0) { + var _uniqueNames2 = new Set(); + pendingComponentWillUpdateWarnings.forEach(function(fiber) { + _uniqueNames2.add(getComponentName(fiber) || "Component"); + didWarnAboutDeprecatedLifecycles.add(fiber.type); + }); + + var _sortedNames2 = Array.from(_uniqueNames2) + .sort() + .join(", "); + + warning( + false, + "componentWillUpdate is deprecated and will be removed in the next major version. " + + "Use componentDidUpdate instead. As a temporary workaround, " + + "you can rename to UNSAFE_componentWillUpdate." + + "\n\nPlease update the following components: %s" + + "\n\nLearn more about this warning here:" + + "\nhttps://fb.me/react-async-component-lifecycle-hooks", + _sortedNames2 ); - 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; + + pendingComponentWillUpdateWarnings = []; } - } + }; - function updatePortal(returnFiber, current, portal, expirationTime) { + ReactStrictModeWarnings.recordDeprecationWarnings = function( + fiber, + instance + ) { + // Dedup strategy: Warn once per component. + if (didWarnAboutDeprecatedLifecycles.has(fiber.type)) { + return; + } + + // Don't warn about react-lifecycles-compat polyfilled components. + // Note that it is sufficient to check for the presence of a + // single lifecycle, componentWillMount, with the polyfill flag. if ( - current === null || - current.tag !== HostPortal || - current.stateNode.containerInfo !== portal.containerInfo || - current.stateNode.implementation !== portal.implementation + typeof instance.componentWillMount === "function" && + instance.componentWillMount.__suppressDeprecationWarning === true ) { - // 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; + return; } - } - 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; + if (typeof instance.componentWillMount === "function") { + pendingComponentWillMountWarnings.push(fiber); } - } - - 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 instance.componentWillReceiveProps === "function") { + pendingComponentWillReceivePropsWarnings.push(fiber); } + if (typeof instance.componentWillUpdate === "function") { + pendingComponentWillUpdateWarnings.push(fiber); + } + }; - 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; - } + ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function( + fiber, + instance + ) { + var strictRoot = getStrictRoot(fiber); + + // Dedup strategy: Warn once per component. + // This is difficult to track any other way since component names + // are often vague and are likely to collide between 3rd party libraries. + // An expand property is probably okay to use here since it's DEV-only, + // and will only be set in the event of serious warnings. + if (didWarnAboutUnsafeLifecycles.has(fiber.type)) { + return; + } - case REACT_RETURN_TYPE: { - var _created4 = createFiberFromReturn( - newChild, - returnFiber.internalContextTag, - expirationTime - ); - _created4.type = newChild.value; - _created4["return"] = returnFiber; - return _created4; - } + // Don't warn about react-lifecycles-compat polyfilled components. + // Note that it is sufficient to check for the presence of a + // single lifecycle, componentWillMount, with the polyfill flag. + if ( + typeof instance.componentWillMount === "function" && + instance.componentWillMount.__suppressDeprecationWarning === true + ) { + return; + } - case REACT_PORTAL_TYPE: { - var _created5 = createFiberFromPortal( - newChild, - returnFiber.internalContextTag, - expirationTime - ); - _created5["return"] = returnFiber; - return _created5; - } - } + var warningsForRoot = void 0; + if (!pendingUnsafeLifecycleWarnings.has(strictRoot)) { + warningsForRoot = { + UNSAFE_componentWillMount: [], + UNSAFE_componentWillReceiveProps: [], + UNSAFE_componentWillUpdate: [] + }; - if (isArray$1(newChild) || getIteratorFn(newChild)) { - var _created6 = createFiberFromFragment( - newChild, - returnFiber.internalContextTag, - expirationTime, - null - ); - _created6["return"] = returnFiber; - return _created6; - } + pendingUnsafeLifecycleWarnings.set(strictRoot, warningsForRoot); + } else { + warningsForRoot = pendingUnsafeLifecycleWarnings.get(strictRoot); + } - throwOnInvalidObjectType(returnFiber, newChild); + var unsafeLifecycles = []; + if ( + typeof instance.componentWillMount === "function" || + typeof instance.UNSAFE_componentWillMount === "function" + ) { + unsafeLifecycles.push("UNSAFE_componentWillMount"); + } + if ( + typeof instance.componentWillReceiveProps === "function" || + typeof instance.UNSAFE_componentWillReceiveProps === "function" + ) { + unsafeLifecycles.push("UNSAFE_componentWillReceiveProps"); + } + if ( + typeof instance.componentWillUpdate === "function" || + typeof instance.UNSAFE_componentWillUpdate === "function" + ) { + unsafeLifecycles.push("UNSAFE_componentWillUpdate"); } - { - if (typeof newChild === "function") { - warnOnFunctionType(); - } + if (unsafeLifecycles.length > 0) { + unsafeLifecycles.forEach(function(lifecycle) { + warningsForRoot[lifecycle].push(fiber); + }); } + }; +} - return null; - } +var didWarnUpdateInsideUpdate = void 0; - function updateSlot(returnFiber, oldFiber, newChild, expirationTime) { - // Update the fiber if the keys match, otherwise return null. +{ + didWarnUpdateInsideUpdate = false; +} - var key = oldFiber !== null ? oldFiber.key : null; +// Callbacks are not validated until invocation - 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 - ); - } +// 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. - 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; - } - } +function createUpdateQueue(baseState) { + var queue = { + baseState: baseState, + expirationTime: NoWork, + first: null, + last: null, + callbackList: null, + hasForceUpdate: false, + isInitialized: false + }; + { + queue.isProcessing = false; + } + return queue; +} - case REACT_CALL_TYPE: { - if (newChild.key === key) { - return updateCall(returnFiber, oldFiber, newChild, expirationTime); - } else { - return null; - } - } +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; + } +} - 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; - } - } +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); + } - case REACT_PORTAL_TYPE: { - if (newChild.key === key) { - return updatePortal( - returnFiber, - oldFiber, - newChild, - expirationTime - ); - } else { - return 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; - if (isArray$1(newChild) || getIteratorFn(newChild)) { - if (key !== null) { - return 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; + } + } - return updateFragment( - returnFiber, - oldFiber, - newChild, - expirationTime, - null - ); - } + // If there's only one queue, add the update to that queue and exit. + if (queue2 === null) { + insertUpdateIntoQueue(queue1, update); + return; + } - throwOnInvalidObjectType(returnFiber, newChild); - } + // 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 (typeof newChild === "function") { - warnOnFunctionType(); - } - } + // 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; +} - return null; +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 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 - ); - } +function getStateFromUpdate(update, instance, prevState, props) { + var partialState = update.partialState; + if (typeof partialState === "function") { + return partialState.call(instance, prevState, props); + } else { + return partialState; + } +} - 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 - ); - } +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 + }; + } - case REACT_CALL_TYPE: { - var _matchedFiber2 = - existingChildren.get( - newChild.key === null ? newIdx : newChild.key - ) || null; - return updateCall( - returnFiber, - _matchedFiber2, - newChild, - expirationTime - ); - } + { + // Set this flag so we can warn if setState is called inside the update + // function of another setState. + queue.isProcessing = true; + } - 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 - ); - } + // Reset the remaining expiration time. If we skip over any updates, we'll + // increase this accordingly. + queue.expirationTime = NoWork; - case REACT_PORTAL_TYPE: { - var _matchedFiber4 = - existingChildren.get( - newChild.key === null ? newIdx : newChild.key - ) || null; - return updatePortal( - returnFiber, - _matchedFiber4, - newChild, - expirationTime - ); - } + // 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 (isArray$1(newChild) || getIteratorFn(newChild)) { - var _matchedFiber5 = existingChildren.get(newIdx) || null; - return updateFragment( - returnFiber, - _matchedFiber5, - newChild, - expirationTime, - null - ); + if (!didSkip) { + didSkip = true; + queue.baseState = state; } - - throwOnInvalidObjectType(returnFiber, newChild); + // Continue to the next update. + update = update.next; + continue; } - { - if (typeof newChild === "function") { - warnOnFunctionType(); + // 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; } } - return null; - } + // Invoke setState callback an extra time to help detect side-effects. + // Ignore the return value in this case. + if ( + debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.internalContextTag & StrictMode) + ) { + getStateFromUpdate(update, instance, state, props); + } - /** - * 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; + // 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 = Object.assign({}, state, _partialState); + } else { + state = Object.assign(state, _partialState); + } + dontMutatePrevState = false; } } - return knownKeys; + 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; } - 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. + 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; + } - // In this first iteration, we'll just live with hitting the bad case - // (adding everything to a Map) in for every insert/move. + if (!didSkip) { + didSkip = true; + queue.baseState = state; + } - // If you change this code, also update reconcileChildrenIterator() which - // uses the same algorithm. + { + // No longer processing. + queue.isProcessing = false; + } - { - // First, validate keys. - var knownKeys = null; - for (var i = 0; i < newChildren.length; i++) { - var child = newChildren[i]; - knownKeys = warnOnInvalidKey(child, knownKeys); - } - } + return state; +} - var resultingFirstChild = null; - var previousNewFiber = null; +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; + invariant( + typeof _callback === "function", + "Invalid argument passed as callback. Expected a function. Instead " + + "received: %s", + _callback + ); + _callback.call(context); + } +} - 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; - } +var fakeInternalInstance = {}; +var isArray = Array.isArray; - if (newIdx === newChildren.length) { - // We've reached the end of the new children. We can delete the rest. - deleteRemainingChildren(returnFiber, oldFiber); - return resultingFirstChild; - } +var didWarnAboutStateAssignmentForComponent = void 0; +var didWarnAboutUndefinedDerivedState = void 0; +var didWarnAboutUninitializedState = void 0; +var didWarnAboutWillReceivePropsAndDerivedState = void 0; +var warnOnInvalidCallback = void 0; - 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; - } +{ + didWarnAboutStateAssignmentForComponent = {}; + didWarnAboutUndefinedDerivedState = {}; + didWarnAboutUninitializedState = {}; + didWarnAboutWillReceivePropsAndDerivedState = {}; - // Add all children to a key map for quick lookups. - var existingChildren = mapRemainingChildren(returnFiber, oldFiber); + var didWarnOnInvalidCallback = {}; - // 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; - } + warnOnInvalidCallback = function(callback, callerName) { + if (callback === null || typeof callback === "function") { + return; } - - 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); - }); + var key = callerName + "_" + callback; + if (!didWarnOnInvalidCallback[key]) { + warning( + false, + "%s(...): Expected the last optional `callback` argument to be a " + + "function. Instead received: %s.", + callerName, + callback + ); + didWarnOnInvalidCallback[key] = true; } + }; - return resultingFirstChild; - } - - function reconcileChildrenIterator( - returnFiber, - currentFirstChild, - newChildrenIterable, - expirationTime - ) { - // This is the same implementation as reconcileChildrenArray(), - // but using the iterator instead. - - var iteratorFn = getIteratorFn(newChildrenIterable); - invariant( - typeof iteratorFn === "function", - "An object is not an iterable. This error is likely caused by a bug in " + - "React. Please file an issue." - ); + // 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); +} - { - // 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; - } +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"); } - - // 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 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); } + }; - var newChildren = iteratorFn.call(newChildrenIterable); - invariant(newChildren != null, "An iterable object provided no iterator."); - - 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() + function checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldState, + newState, + newContext + ) { + if ( + oldProps === null || + (workInProgress.updateQueue !== null && + workInProgress.updateQueue.hasForceUpdate) ) { - if (oldFiber.index > newIdx) { - nextOldFiber = oldFiber; - oldFiber = null; - } else { - nextOldFiber = oldFiber.sibling; - } - var newFiber = updateSlot( - returnFiber, - oldFiber, - step.value, - expirationTime + // 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 ); - 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; + stopPhaseTimer(); + + { + warning( + shouldUpdate !== undefined, + "%s.shouldComponentUpdate(): Returned undefined instead of a " + + "boolean value. Make sure to return true or false.", + getComponentName(workInProgress) || "Unknown" + ); } - 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; + return shouldUpdate; } - 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; + if (type.prototype && type.prototype.isPureReactComponent) { + return ( + !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) + ); } - // Add all children to a key map for quick lookups. - var existingChildren = mapRemainingChildren(returnFiber, oldFiber); + return true; + } - // 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; + 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 { - previousNewFiber.sibling = _newFiber4; + warning( + false, + "%s(...): No `render` method found on the returned component " + + "instance: you may have forgotten to define `render`.", + name + ); } - previousNewFiber = _newFiber4; } + + 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 noUnsafeComponentWillRecieveProps = + typeof instance.UNSAFE_componentWillRecieveProps !== "function"; + warning( + noUnsafeComponentWillRecieveProps, + "%s has a method called " + + "UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_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 + ); } - 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); - }); + 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) + ); } + } - return resultingFirstChild; + function resetInputPointers(workInProgress, instance) { + instance.props = workInProgress.memoizedProps; + instance.state = workInProgress.memoizedState; } - 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; + 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; } - // 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 + function constructClassInstance(workInProgress, props) { + var ctor = workInProgress.type; + var unmaskedContext = getUnmaskedContext(workInProgress); + var needsContext = isContextConsumer(workInProgress); + var context = needsContext + ? getMaskedContext(workInProgress, unmaskedContext) + : emptyObject; + + // Instantiate twice to help detect side-effects. + if ( + debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.internalContextTag & StrictMode) + ) { + new ctor(props, context); // eslint-disable-line no-new + } + + var instance = new ctor(props, context); + var state = + instance.state !== null && instance.state !== undefined + ? instance.state + : null; + adoptClassInstance(workInProgress, instance); + + { + if ( + typeof ctor.getDerivedStateFromProps === "function" && + state === null + ) { + var componentName = getComponentName(workInProgress) || "Unknown"; + if (!didWarnAboutUninitializedState[componentName]) { + warning( + false, + "%s: Did not properly initialize state during construction. " + + "Expected state to be an object, but it was %s.", + componentName, + instance.state === null ? "null" : "undefined" ); - existing.ref = coerceRef(child, element); - existing["return"] = returnFiber; - { - existing._debugSource = element._source; - existing._debugOwner = element._owner; - } - return existing; - } else { - deleteRemainingChildren(returnFiber, child); - break; + didWarnAboutUninitializedState[componentName] = true; } - } 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 + workInProgress.memoizedState = state; + + var partialState = callGetDerivedStateFromProps( + workInProgress, + instance, + props + ); + + if (partialState !== null && partialState !== undefined) { + // Render-phase updates (like this) should not be added to the update queue, + // So that multiple render passes do not enqueue multiple updates. + // Instead, just synchronously merge the returned state into the instance. + workInProgress.memoizedState = Object.assign( + {}, + workInProgress.memoizedState, + partialState ); - _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; + // 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); } - var created = createFiberFromCall( - call, - returnFiber.internalContextTag, - expirationTime - ); - created["return"] = returnFiber; - return created; + return instance; } - 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); - } + function callComponentWillMount(workInProgress, instance) { + startPhaseTimer(workInProgress, "componentWillMount"); + var oldState = instance.state; + + if (typeof instance.componentWillMount === "function") { + instance.componentWillMount(); + } else { + instance.UNSAFE_componentWillMount(); } - var created = createFiberFromReturn( - returnNode, - returnFiber.internalContextTag, - expirationTime - ); - created.type = returnNode.value; - created["return"] = returnFiber; - return created; - } + stopPhaseTimer(); - 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); + 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) + ); } - child = child.sibling; + updater.enqueueReplaceState(instance, instance.state, null); } - - 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 + function callComponentWillReceiveProps( + workInProgress, + instance, + newProps, + newContext ) { - // 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. + var oldState = instance.state; + if (typeof instance.componentWillReceiveProps === "function") { + startPhaseTimer(workInProgress, "componentWillReceiveProps"); + instance.componentWillReceiveProps(newProps, newContext); + stopPhaseTimer(); + } else { + startPhaseTimer(workInProgress, "componentWillReceiveProps"); + instance.UNSAFE_componentWillReceiveProps(newProps, newContext); + stopPhaseTimer(); + } - // 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; + 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); } + } - // Handle object types - var isObject = typeof newChild === "object" && newChild !== null; + function callGetDerivedStateFromProps(workInProgress, instance, props) { + var type = workInProgress.type; - if (isObject) { - switch (newChild.$$typeof) { - case REACT_ELEMENT_TYPE: - return placeSingleChild( - reconcileSingleElement( - returnFiber, - currentFirstChild, - newChild, - expirationTime - ) - ); + if (typeof type.getDerivedStateFromProps === "function") { + { + // Don't warn about react-lifecycles-compat polyfilled components + if ( + (typeof instance.componentWillReceiveProps === "function" && + instance.componentWillReceiveProps.__suppressDeprecationWarning !== + true) || + typeof instance.UNSAFE_componentWillReceiveProps === "function" + ) { + var componentName = getComponentName(workInProgress) || "Unknown"; + if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) { + warning( + false, + "%s: Defines both componentWillReceiveProps() and static " + + "getDerivedStateFromProps() methods. We recommend using " + + "only getDerivedStateFromProps().", + componentName + ); + didWarnAboutWillReceivePropsAndDerivedState[componentName] = true; + } + } + } - 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 ( + debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.internalContextTag & StrictMode) + ) { + // Invoke method an extra time to help detect side-effects. + type.getDerivedStateFromProps.call( + null, + props, + workInProgress.memoizedState + ); } - } - if (typeof newChild === "string" || typeof newChild === "number") { - return placeSingleChild( - reconcileSingleTextNode( - returnFiber, - currentFirstChild, - "" + newChild, - expirationTime - ) + var partialState = type.getDerivedStateFromProps.call( + null, + props, + workInProgress.memoizedState ); - } - if (isArray$1(newChild)) { - return reconcileChildrenArray( - returnFiber, - currentFirstChild, - newChild, - expirationTime - ); - } + { + if (partialState === undefined) { + var _componentName = getComponentName(workInProgress) || "Unknown"; + if (!didWarnAboutUndefinedDerivedState[_componentName]) { + warning( + false, + "%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. " + + "You have returned undefined.", + _componentName + ); + didWarnAboutUndefinedDerivedState[_componentName] = _componentName; + } + } + } - if (getIteratorFn(newChild)) { - return reconcileChildrenIterator( - returnFiber, - currentFirstChild, - newChild, - expirationTime - ); + return partialState; } + } - if (isObject) { - throwOnInvalidObjectType(returnFiber, newChild); - } + // Invokes the mount life-cycles on a previously never rendered instance. + function mountClassInstance(workInProgress, renderExpirationTime) { + var current = workInProgress.alternate; { - if (typeof newChild === "function") { - warnOnFunctionType(); - } + checkClassInstance(workInProgress); } - 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" - ); + + var instance = workInProgress.stateNode; + var props = workInProgress.pendingProps; + var unmaskedContext = getUnmaskedContext(workInProgress); + + instance.props = props; + instance.state = workInProgress.memoizedState; + instance.refs = emptyObject; + instance.context = getMaskedContext(workInProgress, unmaskedContext); + + if (workInProgress.type != null && workInProgress.type.prototype != null) { + var prototype = workInProgress.type.prototype; + + if (enableAsyncSubtreeAPI) { + if (prototype.unstable_isAsyncReactComponent === true) { + workInProgress.internalContextTag |= AsyncUpdates; + workInProgress.internalContextTag |= StrictMode; } } } - // Remaining cases are all treated as empty. - return deleteRemainingChildren(returnFiber, currentFirstChild); + { + if (workInProgress.internalContextTag & StrictMode) { + ReactStrictModeWarnings.recordUnsafeLifecycleWarnings( + workInProgress, + instance + ); + } + + if (warnAboutDeprecatedLifecycles) { + ReactStrictModeWarnings.recordDeprecationWarnings( + workInProgress, + instance + ); + } + } + + // In order to support react-lifecycles-compat polyfilled components, + // Unsafe lifecycles should not be invoked for any component with the new gDSFP. + if ( + (typeof instance.UNSAFE_componentWillMount === "function" || + typeof instance.componentWillMount === "function") && + typeof workInProgress.type.getDerivedStateFromProps !== "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; + } } - return reconcileChildFibers; -} + // 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); -var reconcileChildFibers = ChildReconciler(true); -var mountChildFibers = ChildReconciler(false); + // 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); -function cloneChildFibers(current, workInProgress) { - invariant( - current === null || workInProgress.child === current.child, - "Resuming work not yet implemented." - ); + // const oldContext = instance.context; + // const oldProps = workInProgress.memoizedProps; - if (workInProgress.child === null) { - return; - } + // if ( + // typeof instance.componentWillReceiveProps === 'function' && + // (oldProps !== newProps || oldContext !== newContext) + // ) { + // callComponentWillReceiveProps( + // workInProgress, + // instance, + // newProps, + // newContext, + // ); + // } - var currentChild = workInProgress.child; - var newChild = createWorkInProgress( - currentChild, - currentChild.pendingProps, - currentChild.expirationTime - ); - workInProgress.child = newChild; + // // Process the update queue before calling shouldComponentUpdate + // const updateQueue = workInProgress.updateQueue; + // if (updateQueue !== null) { + // newState = processUpdateQueue( + // workInProgress, + // updateQueue, + // instance, + // newState, + // newProps, + // priorityLevel, + // ); + // } - 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; -} + // // TODO: Should we deal with a setState that happened after the last + // // componentWillMount and before this componentWillMount? Probably + // // unsupported anyway. -{ - var warnedAboutStatelessRefs = {}; -} + // 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; + // } -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; + // // Update the input pointers now so that they are correct when we call + // // componentWillMount + // instance.props = newProps; + // instance.state = newState; + // instance.context = newContext; - var _ReactFiberClassCompo = ReactFiberClassComponent( - scheduleWork, - computeExpirationForFiber, - memoizeProps, - memoizeState - ), - adoptClassInstance = _ReactFiberClassCompo.adoptClassInstance, - constructClassInstance = _ReactFiberClassCompo.constructClassInstance, - mountClassInstance = _ReactFiberClassCompo.mountClassInstance, - updateClassInstance = _ReactFiberClassCompo.updateClassInstance; + // 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, + // ); + // } + // } - // TODO: Remove this and use reconcileChildrenAtExpirationTime directly. + // if (typeof instance.componentDidMount === 'function') { + // workInProgress.effectTag |= Update; + // } - function reconcileChildren(current, workInProgress, nextChildren) { - reconcileChildrenAtExpirationTime( - current, - workInProgress, - nextChildren, - workInProgress.expirationTime - ); - } + // instance.state = newState; - 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. + // return true; + // } - // 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. - } else if ( - nextChildren === null || - workInProgress.memoizedProps === nextChildren - ) { - return bailoutOnAlreadyFinishedWork(current, workInProgress); - } - reconcileChildren(current, workInProgress, nextChildren); - memoizeProps(workInProgress, nextChildren); - return workInProgress.child; - } + // 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); - function markRef(current, workInProgress) { - var ref = workInProgress.ref; - if (ref !== null && (!current || current.ref !== ref)) { - // Schedule a Ref effect - workInProgress.effectTag |= Ref; - } - } + var oldProps = workInProgress.memoizedProps; + var newProps = workInProgress.pendingProps; + var oldContext = instance.context; + var newUnmaskedContext = getUnmaskedContext(workInProgress); + var newContext = getMaskedContext(workInProgress, newUnmaskedContext); - function updateFunctionalComponent(current, workInProgress) { - var fn = workInProgress.type; - var nextProps = workInProgress.pendingProps; + // 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 (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 (workInProgress.memoizedProps === nextProps) { - return bailoutOnAlreadyFinishedWork(current, workInProgress); + // In order to support react-lifecycles-compat polyfilled components, + // Unsafe lifecycles should not be invoked for any component with the new gDSFP. + if ( + (typeof instance.UNSAFE_componentWillReceiveProps === "function" || + typeof instance.componentWillReceiveProps === "function") && + typeof workInProgress.type.getDerivedStateFromProps !== "function" + ) { + if (oldProps !== newProps || oldContext !== newContext) { + callComponentWillReceiveProps( + workInProgress, + instance, + newProps, + newContext + ); } - // 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); + var partialState = void 0; + if (oldProps !== newProps) { + partialState = callGetDerivedStateFromProps( + workInProgress, + instance, + newProps + ); } - // 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( + // 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; } - return finishClassComponent( - current, - workInProgress, - shouldUpdate, - hasContext - ); - } - function finishClassComponent( - current, - workInProgress, - shouldUpdate, - hasContext - ) { - // Refs should update even if shouldComponentUpdate returns false - markRef(current, workInProgress); + if (partialState !== null && partialState !== undefined) { + // Render-phase updates (like this) should not be added to the update queue, + // So that multiple render passes do not enqueue multiple updates. + // Instead, just synchronously merge the returned state into the instance. + newState = + newState === null || newState === undefined + ? partialState + : Object.assign({}, newState, partialState); + } - if (!shouldUpdate) { - // Context providers should defer to sCU for rendering - if (hasContext) { - invalidateContextProvider(workInProgress, false); + 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 bailoutOnAlreadyFinishedWork(current, workInProgress); + return false; } - var instance = workInProgress.stateNode; + var shouldUpdate = checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldState, + newState, + newContext + ); - // Rerender - ReactCurrentOwner.current = workInProgress; - var nextChildren = void 0; - { - ReactDebugCurrentFiber.setCurrentPhase("render"); - nextChildren = instance.render(); - if (debugRenderPhaseSideEffects) { - instance.render(); + if (shouldUpdate) { + // In order to support react-lifecycles-compat polyfilled components, + // Unsafe lifecycles should not be invoked for any component with the new gDSFP. + if ( + (typeof instance.UNSAFE_componentWillUpdate === "function" || + typeof instance.componentWillUpdate === "function") && + typeof workInProgress.type.getDerivedStateFromProps !== "function" + ) { + if (typeof instance.componentWillUpdate === "function") { + startPhaseTimer(workInProgress, "componentWillUpdate"); + instance.componentWillUpdate(newProps, newState, newContext); + stopPhaseTimer(); + } else { + startPhaseTimer(workInProgress, "componentWillUpdate"); + instance.UNSAFE_componentWillUpdate(newProps, newState, newContext); + stopPhaseTimer(); + } + } + 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; + } } - 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); + // 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); } - return workInProgress.child; + // 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; } - 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); + return { + adoptClassInstance: adoptClassInstance, + callGetDerivedStateFromProps: callGetDerivedStateFromProps, + constructClassInstance: constructClassInstance, + mountClassInstance: mountClassInstance, + // resumeMountClassInstance, + updateClassInstance: updateClassInstance + }; +}; + +var getCurrentFiberStackAddendum$1 = + ReactDebugCurrentFiber.getCurrentFiberStackAddendum; + +var didWarnAboutMaps = void 0; +var ownerHasKeyUseWarning = void 0; +var ownerHasFunctionTypeWarning = void 0; +var warnForMissingKey = function(child) {}; + +{ + 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. + */ + ownerHasKeyUseWarning = {}; + ownerHasFunctionTypeWarning = {}; + + warnForMissingKey = function(child) { + if (child === null || typeof child !== "object") { + return; } - pushHostContainer(workInProgress, root.containerInfo); - } + if (!child._store || child._store.validated || child.key != null) { + return; + } + invariant( + typeof child._store === "object", + "React Component in warnForMissingKey should have a _store. " + + "This error is likely caused by a bug in React. Please file an issue." + ); + child._store.validated = true; - 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. + 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; - // 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; + 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() + ); + }; +} - // 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 +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; + invariant( + ownerFiber.tag === ClassComponent, + "Stateless function components cannot have refs." ); - } else { - // Otherwise reset hydration state in case we aborted and resumed another - // root. - resetHydrationState(); - reconcileChildren(current, workInProgress, element); + inst = ownerFiber.stateNode; } - memoizeState(workInProgress, state); - return workInProgress.child; + invariant( + inst, + "Missing owner for string ref %s. This error is likely caused by a " + + "bug in React. Please file an issue.", + mixedRef + ); + 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 { + invariant( + typeof mixedRef === "string", + "Expected ref to be a function or a string." + ); + invariant( + element._owner, + "Element ref was specified as a string (%s) but no owner was set. This could happen for one of" + + " the following reasons:\n" + + "1. You may be adding a ref to a functional component\n" + + "2. You may be adding a ref to a component that was not created inside a component's render method\n" + + "3. You have multiple copies of React loaded\n" + + "See https://fb.me/react-refs-must-have-owner for more information.", + mixedRef + ); } - resetHydrationState(); - // If there is no update queue, that's a bailout because the root has no props. - return bailoutOnAlreadyFinishedWork(current, workInProgress); } + return mixedRef; +} - function updateHostComponent(current, workInProgress, renderExpirationTime) { - pushHostContext(workInProgress); - - if (current === null) { - tryToClaimNextHydratableInstance(workInProgress); +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 + ); + } +} - var type = workInProgress.type; - var memoizedProps = workInProgress.memoizedProps; - var nextProps = workInProgress.pendingProps; - var prevProps = current !== null ? current.memoizedProps : null; +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 (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 (memoizedProps === nextProps) { - return bailoutOnAlreadyFinishedWork(current, workInProgress); - } + if (ownerHasFunctionTypeWarning[currentComponentErrorInfo]) { + return; + } + ownerHasFunctionTypeWarning[currentComponentErrorInfo] = true; - var nextChildren = nextProps.children; - var isDirectTextChild = shouldSetTextContent(type, nextProps); + 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() || "" + ); +} - 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; +// 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; + } - 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. + function deleteRemainingChildren(returnFiber, currentFirstChild) { + if (!shouldTrackSideEffects) { + // Noop. return null; } - reconcileChildren(current, workInProgress, nextChildren); - memoizeProps(workInProgress, nextProps); - return workInProgress.child; - } - - function updateHostText(current, workInProgress) { - if (current === null) { - tryToClaimNextHydratableInstance(workInProgress); + // 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; } - var nextProps = workInProgress.pendingProps; - 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 - ) { - invariant( - current === null, - "An indeterminate component should never have mounted. This error is " + - "likely caused by a bug in React. Please file an issue." - ); - var fn = workInProgress.type; - var props = workInProgress.pendingProps; - var unmaskedContext = getUnmaskedContext(workInProgress); - var context = getMaskedContext(workInProgress, unmaskedContext); - - var value; + 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(); - { - 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 - ); + var existingChild = currentFirstChild; + while (existingChild !== null) { + if (existingChild.key !== null) { + existingChildren.set(existingChild.key, existingChild); + } else { + existingChildren.set(existingChild.index, existingChild); } - ReactCurrentOwner.current = workInProgress; - value = fn(props, context); + existingChild = existingChild.sibling; } - // 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; + return existingChildren; + } - 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 + "`."; - } + 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; + } - 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() - ); - } - } + 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; } - reconcileChildren(current, workInProgress, value); - memoizeProps(workInProgress, props); - return workInProgress.child; + } else { + // This is an insertion. + newFiber.effectTag = Placement; + return lastPlacedIndex; } } - 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. - } else if (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); + 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; + } - 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 + 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 { - workInProgress.stateNode = reconcileChildFibers( - workInProgress, - workInProgress.stateNode, - nextChildren, - renderExpirationTime - ); + // Update + var existing = useFiber(current, textContent, expirationTime); + existing["return"] = returnFiber; + return existing; } - - 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. - } else if (workInProgress.memoizedProps === nextChildren) { - return bailoutOnAlreadyFinishedWork(current, workInProgress); + 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; } + } - 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 + 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 ); - memoizeProps(workInProgress, nextChildren); + created["return"] = returnFiber; + return created; } else { - reconcileChildren(current, workInProgress, nextChildren); - memoizeProps(workInProgress, nextChildren); + // Update + var existing = useFiber(current, portal.children || [], expirationTime); + existing["return"] = returnFiber; + return existing; } - 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 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 bailoutOnAlreadyFinishedWork(current, workInProgress) { - cancelWorkTimer(workInProgress); + 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; + } - // 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; - // } + if (typeof newChild === "object" && newChild !== null) { + switch (newChild.$$typeof) { + case REACT_ELEMENT_TYPE: { + var _created = createFiberFromElement( + newChild, + returnFiber.internalContextTag, + expirationTime + ); + _created.ref = coerceRef(null, newChild); + _created["return"] = returnFiber; + return _created; + } + case REACT_PORTAL_TYPE: { + var _created2 = createFiberFromPortal( + newChild, + returnFiber.internalContextTag, + expirationTime + ); + _created2["return"] = returnFiber; + return _created2; + } + } - cloneChildFibers(current, workInProgress); - return workInProgress.child; - } + if (isArray$1(newChild) || getIteratorFn(newChild)) { + var _created3 = createFiberFromFragment( + newChild, + returnFiber.internalContextTag, + expirationTime, + null + ); + _created3["return"] = returnFiber; + return _created3; + } - function bailoutOnLowPriority(current, workInProgress) { - cancelWorkTimer(workInProgress); + throwOnInvalidObjectType(returnFiber, newChild); + } - // 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; + { + if (typeof newChild === "function") { + warnOnFunctionType(); + } } - // 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; + return null; } - function memoizeState(workInProgress, nextState) { - workInProgress.memoizedState = nextState; - // Don't reset the updateQueue, in case there are pending updates. Resetting - // is handled by processUpdateQueue. - } + function updateSlot(returnFiber, oldFiber, newChild, expirationTime) { + // Update the fiber if the keys match, otherwise return null. - function beginWork(current, workInProgress, renderExpirationTime) { - if ( - workInProgress.expirationTime === NoWork || - workInProgress.expirationTime > renderExpirationTime - ) { - return bailoutOnLowPriority(current, workInProgress); - } + var key = oldFiber !== null ? oldFiber.key : null; - 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. + 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; - 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." - ); + } + return updateTextNode( + returnFiber, + oldFiber, + "" + newChild, + expirationTime + ); } - } - 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." - ); - } + 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_PORTAL_TYPE: { + if (newChild.key === key) { + return updatePortal( + returnFiber, + oldFiber, + newChild, + expirationTime + ); + } else { + return null; + } + } + } - // Add an error effect so we can handle the error during the commit phase - workInProgress.effectTag |= Err; + if (isArray$1(newChild) || getIteratorFn(newChild)) { + if (key !== null) { + return null; + } - // 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; - } + return updateFragment( + returnFiber, + oldFiber, + newChild, + expirationTime, + null + ); + } - if ( - workInProgress.expirationTime === NoWork || - workInProgress.expirationTime > renderExpirationTime - ) { - return bailoutOnLowPriority(current, workInProgress); + throwOnInvalidObjectType(returnFiber, newChild); } - // 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; + { + if (typeof newChild === "function") { + warnOnFunctionType(); + } } - return workInProgress.child; + return null; } - return { - beginWork: beginWork, - beginFailedWork: beginFailedWork - }; -}; + 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 + ); + } -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; + 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_PORTAL_TYPE: { + var _matchedFiber2 = + existingChildren.get( + newChild.key === null ? newIdx : newChild.key + ) || null; + return updatePortal( + returnFiber, + _matchedFiber2, + newChild, + expirationTime + ); + } + } - function markUpdate(workInProgress) { - // Tag the fiber with an update effect. This turns a Placement into - // an UpdateAndPlacement. - workInProgress.effectTag |= Update; - } + if (isArray$1(newChild) || getIteratorFn(newChild)) { + var _matchedFiber3 = existingChildren.get(newIdx) || null; + return updateFragment( + returnFiber, + _matchedFiber3, + newChild, + expirationTime, + null + ); + } - function markRef(workInProgress) { - workInProgress.effectTag |= Ref; - } + throwOnInvalidObjectType(returnFiber, newChild); + } - function appendAllReturns(returns, workInProgress) { - var node = workInProgress.stateNode; - if (node) { - node["return"] = workInProgress; + { + if (typeof newChild === "function") { + warnOnFunctionType(); + } } - 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; + + return null; + } + + /** + * Warns if there is a duplicate or missing key + */ + function warnOnInvalidKey(child, knownKeys) { + { + if (typeof child !== "object" || child === null) { + return knownKeys; } - while (node.sibling === null) { - if (node["return"] === null || node["return"] === workInProgress) { - return; - } - node = node["return"]; + switch (child.$$typeof) { + case REACT_ELEMENT_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; } - node.sibling["return"] = node["return"]; - node = node.sibling; } + return knownKeys; } - function moveCallToHandlerPhase( - current, - workInProgress, - renderExpirationTime + function reconcileChildrenArray( + returnFiber, + currentFirstChild, + newChildren, + expirationTime ) { - var call = workInProgress.memoizedProps; - invariant( - call, - "Should be resolved by now. This error is likely caused by a bug in " + - "React. Please file an issue." - ); + // 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. - // 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; + // 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. - // 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); + // In this first iteration, we'll just live with hitting the bad case + // (adding everything to a Map) in for every insert/move. - var currentFirstChild = current !== null ? current.child : null; - workInProgress.child = reconcileChildFibers( - workInProgress, - currentFirstChild, - nextChildren, - renderExpirationTime - ); - return workInProgress.child; - } + // If you change this code, also update reconcileChildrenIterator() which + // uses the same algorithm. - 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"]; + { + // First, validate keys. + var knownKeys = null; + for (var i = 0; i < newChildren.length; i++) { + var child = newChildren[i]; + knownKeys = warnOnInvalidKey(child, knownKeys); } - 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); + 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; } - }; - 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); + 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); } - }; - } else { - invariant(false, "Mutating reconciler is disabled."); + } + 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; } - } 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. + if (newIdx === newChildren.length) { + // We've reached the end of the new children. We can delete the rest. + deleteRemainingChildren(returnFiber, oldFiber); + return resultingFirstChild; + } - 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; + 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; } - }; - updateHostContainer = function(workInProgress) { - var portalOrRoot = workInProgress.stateNode; - var childrenUnchanged = workInProgress.firstEffect === null; - if (childrenUnchanged) { - // No changes, just reuse the existing instance. + lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx); + if (previousNewFiber === null) { + // TODO: Move out of the loop. This only happens for the first run. + resultingFirstChild = _newFiber; } 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); + previousNewFiber.sibling = _newFiber; } - }; - 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); + 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 + ); } } - }; - 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); + lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx); + if (previousNewFiber === null) { + resultingFirstChild = _newFiber2; + } else { + previousNewFiber.sibling = _newFiber2; } - }; - } else { - invariant(false, "Persistent reconciler is disabled."); + previousNewFiber = _newFiber2; + } } - } 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 - }; + + 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); + invariant( + typeof iteratorFn === "function", + "An object is not an iterable. This error is likely caused by a bug in " + + "React. Please file an issue." + ); + + { + // 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); + invariant(newChildren != null, "An iterable object provided no iterator."); + + 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 _created4 = createFiberFromElement( + element, + returnFiber.internalContextTag, + expirationTime + ); + _created4.ref = coerceRef(currentFirstChild, element); + _created4["return"] = returnFiber; + return _created4; + } + } + + 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_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) { + invariant( + current === null || workInProgress.child === current.child, + "Resuming work not yet implemented." + ); + + 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 stack = []; +var index$1 = -1; + +var rendererSigil = void 0; +{ + // Use this to detect multiple renderers using the same context + rendererSigil = {}; +} + +function pushProvider(providerFiber) { + index$1 += 1; + stack[index$1] = providerFiber; + var context = providerFiber.type.context; + context.currentValue = providerFiber.pendingProps.value; + context.changedBits = providerFiber.stateNode; + + { + warning( + context._currentRenderer === null || + context._currentRenderer === rendererSigil, + "Detected multiple renderers concurrently rendering the " + + "same context provider. This is currently unsupported." + ); + context._currentRenderer = rendererSigil; + } +} + +function popProvider(providerFiber) { + { + warning( + index$1 > -1 && providerFiber === stack[index$1], + "Unexpected pop." + ); + } + stack[index$1] = null; + index$1 -= 1; + var context = providerFiber.type.context; + if (index$1 < 0) { + context.currentValue = context.defaultValue; + context.changedBits = 0; + } else { + var previousProviderFiber = stack[index$1]; + context.currentValue = previousProviderFiber.pendingProps.value; + context.changedBits = previousProviderFiber.stateNode; + } +} + +function resetProviderStack() { + for (var i = index$1; i > -1; i--) { + var providerFiber = stack[i]; + var context = providerFiber.type.context; + context.currentValue = context.defaultValue; + context.changedBits = 0; + stack[i] = null; + { + context._currentRenderer = null; + } + } +} + +var didWarnAboutBadClass = void 0; +var didWarnAboutGetDerivedStateOnFunctionalComponent = void 0; +var didWarnAboutStatelessRefs = void 0; + +{ + didWarnAboutBadClass = {}; + didWarnAboutGetDerivedStateOnFunctionalComponent = {}; + didWarnAboutStatelessRefs = {}; +} + +var ReactFiberBeginWork = function( + config, + hostContext, + hydrationContext, + scheduleWork, + computeExpirationForFiber +) { + var shouldSetTextContent = config.shouldSetTextContent, + 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, + callGetDerivedStateFromProps = + _ReactFiberClassCompo.callGetDerivedStateFromProps, + 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. + } else if (workInProgress.memoizedProps === nextChildren) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + reconcileChildren(current, workInProgress, nextChildren); + memoizeProps(workInProgress, nextChildren); + return workInProgress.child; + } + + function updateMode(current, workInProgress) { + var nextChildren = workInProgress.pendingProps.children; + 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 ( + 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; + + 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 { - invariant(false, "Noop reconciler is disabled."); + if (workInProgress.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 = void 0; + + { + 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 || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.internalContextTag & StrictMode) + ) { + 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; + 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 (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 && + workInProgress.internalContextTag & AsyncUpdates && + 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 completeWork(current, workInProgress, renderExpirationTime) { - var newProps = workInProgress.pendingProps; - 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; - } + function updateHostText(current, workInProgress) { + if (current === null) { + tryToClaimNextHydratableInstance(workInProgress); + } + var nextProps = workInProgress.pendingProps; + memoizeProps(workInProgress, nextProps); + // Nothing to do here. This is terminal. We'll do the completion step + // immediately after. + return 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; + function mountIndeterminateComponent( + current, + workInProgress, + renderExpirationTime + ) { + invariant( + current === null, + "An indeterminate component should never have mounted. This error is " + + "likely caused by a bug in React. Please file an issue." + ); + var fn = workInProgress.type; + var props = workInProgress.pendingProps; + var unmaskedContext = getUnmaskedContext(workInProgress); + var context = getMaskedContext(workInProgress, unmaskedContext); + + var value = void 0; + + { + if (fn.prototype && typeof fn.prototype.render === "function") { + var componentName = getComponentName(workInProgress) || "Unknown"; + + if (!didWarnAboutBadClass[componentName]) { + 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 + ); + didWarnAboutBadClass[componentName] = true; } - 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 + 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" && + value.$$typeof === undefined + ) { + var Component = workInProgress.type; + + // Proceed under the assumption that this is a class instance + workInProgress.tag = ClassComponent; + + workInProgress.memoizedState = + value.state !== null && value.state !== undefined ? value.state : null; + + if (typeof Component.getDerivedStateFromProps === "function") { + var partialState = callGetDerivedStateFromProps( + workInProgress, + value, + props + ); + + if (partialState !== null && partialState !== undefined) { + workInProgress.memoizedState = Object.assign( + {}, + workInProgress.memoizedState, + partialState ); + } + } - updateHostComponent( - current, - workInProgress, - updatePayload, - type, - oldProps, - newProps, - rootContainerInstance + // 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 + "`."; + } - if (current.ref !== workInProgress.ref) { - markRef(workInProgress); + var warningKey = ownerName || workInProgress._debugID || ""; + var debugSource = workInProgress._debugSource; + if (debugSource) { + warningKey = debugSource.fileName + ":" + debugSource.lineNumber; } - } else { - if (!newProps) { - invariant( - workInProgress.stateNode !== null, - "We must have new props for new mounts. This error is likely " + - "caused by a bug in React. Please file an issue." + if (!didWarnAboutStatelessRefs[warningKey]) { + didWarnAboutStatelessRefs[warningKey] = true; + warning( + false, + "Stateless function components cannot be given refs. " + + "Attempts to access this ref will fail.%s%s", + info, + ReactDebugCurrentFiber.getCurrentFiberStackAddendum() ); - // 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 + if (typeof fn.getDerivedStateFromProps === "function") { + var _componentName = getComponentName(workInProgress) || "Unknown"; + + if ( + !didWarnAboutGetDerivedStateOnFunctionalComponent[_componentName] + ) { + warning( + false, + "%s: Stateless functional components do not support getDerivedStateFromProps.", + _componentName ); + didWarnAboutGetDerivedStateOnFunctionalComponent[ + _componentName + ] = true; + } + } + } + reconcileChildren(current, workInProgress, value); + memoizeProps(workInProgress, props); + return workInProgress.child; + } + } + + function updateCallComponent(current, workInProgress, renderExpirationTime) { + var nextProps = 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. + } else if (workInProgress.memoizedProps === nextProps) { + nextProps = 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 = nextProps.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, + current.stateNode, + nextChildren, + renderExpirationTime + ); + } + + memoizeProps(workInProgress, nextProps); + // 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. + } else if (workInProgress.memoizedProps === nextChildren) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } - appendAllChildren(_instance, 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; + } - // 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); + function propagateContextChange( + workInProgress, + context, + changedBits, + renderExpirationTime + ) { + if (enableNewContextAPI) { + var _fiber = workInProgress.child; + while (_fiber !== null) { + var nextFiber = void 0; + // Visit this fiber. + switch (_fiber.tag) { + case ContextConsumer: + // Check if the context matches. + var observedBits = _fiber.stateNode | 0; + if (_fiber.type === context && (observedBits & changedBits) !== 0) { + // Update the expiration time of all the ancestors, including + // the alternates. + var node = _fiber; + while (node !== null) { + var alternate = node.alternate; + if ( + node.expirationTime === NoWork || + node.expirationTime > renderExpirationTime + ) { + node.expirationTime = renderExpirationTime; + if ( + alternate !== null && + (alternate.expirationTime === NoWork || + alternate.expirationTime > renderExpirationTime) + ) { + alternate.expirationTime = renderExpirationTime; + } + } else if ( + alternate !== null && + (alternate.expirationTime === NoWork || + alternate.expirationTime > renderExpirationTime) + ) { + alternate.expirationTime = renderExpirationTime; + } else { + // Neither alternate was updated, which means the rest of the + // ancestor path already has sufficient priority. + break; + } + node = node["return"]; + } + // Don't scan deeper than a matching consumer. When we render the + // consumer, we'll continue scanning from that point. This way the + // scanning work is time-sliced. + nextFiber = null; + } else { + // Traverse down. + nextFiber = _fiber.child; } - workInProgress.stateNode = _instance; - } - - if (workInProgress.ref !== null) { - // If there is a ref on a host node we need to schedule a callback - markRef(workInProgress); + break; + case ContextProvider: + // Don't scan deeper if this is a matching provider + nextFiber = + _fiber.type === workInProgress.type ? null : _fiber.child; + break; + default: + // Traverse down. + nextFiber = _fiber.child; + break; + } + if (nextFiber !== null) { + // Set the return pointer of the child to the work-in-progress fiber. + nextFiber["return"] = _fiber; + } else { + // No child. Traverse to next sibling. + nextFiber = _fiber; + while (nextFiber !== null) { + if (nextFiber === workInProgress) { + // We're back to the root of this subtree. Exit. + nextFiber = null; + break; + } + var sibling = nextFiber.sibling; + if (sibling !== null) { + nextFiber = sibling; + break; + } + // No more siblings. Traverse up. + nextFiber = nextFiber["return"]; } } - return null; + _fiber = nextFiber; } - 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); + } + } + + function updateContextProvider( + current, + workInProgress, + renderExpirationTime + ) { + if (enableNewContextAPI) { + var providerType = workInProgress.type; + var context = providerType.context; + + var newProps = workInProgress.pendingProps; + var oldProps = 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. + } else if (oldProps === newProps) { + workInProgress.stateNode = 0; + pushProvider(workInProgress); + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + workInProgress.memoizedProps = newProps; + + var newValue = newProps.value; + + var changedBits = void 0; + if (oldProps === null) { + // Initial render + changedBits = MAX_SIGNED_31_BIT_INT; + } else { + var oldValue = oldProps.value; + // Use Object.is to compare the new context value to the old value. + // Inlined Object.is polyfill. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is + if ( + (oldValue === newValue && + (oldValue !== 0 || 1 / oldValue === 1 / newValue)) || + (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare + ) { + // No change. + changedBits = 0; } else { - if (typeof newText !== "string") { - invariant( - workInProgress.stateNode !== null, - "We must have new props for new mounts. This error is likely " + - "caused by a bug in React. Please file an issue." + changedBits = + context.calculateChangedBits !== null + ? context.calculateChangedBits(oldValue, newValue) + : MAX_SIGNED_31_BIT_INT; + { + warning( + (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits, + "calculateChangedBits: Expected the return value to be a " + + "31-bit integer. Instead received: %s", + changedBits ); - // 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 + changedBits |= 0; + + if (changedBits !== 0) { + propagateContextChange( + workInProgress, + context, + changedBits, + renderExpirationTime ); } } - return null; } + + workInProgress.stateNode = changedBits; + pushProvider(workInProgress); + + if (oldProps !== null && oldProps.children === newProps.children) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + var newChildren = newProps.children; + reconcileChildren(current, workInProgress, newChildren); + return workInProgress.child; + } else { + return null; + } + } + + function updateContextConsumer( + current, + workInProgress, + renderExpirationTime + ) { + if (enableNewContextAPI) { + var context = workInProgress.type; + var newProps = workInProgress.pendingProps; + + var newValue = context.currentValue; + var changedBits = context.changedBits; + + if (changedBits !== 0) { + // Context change propagation stops at matching consumers, for time- + // slicing. Continue the propagation here. + propagateContextChange( + workInProgress, + context, + changedBits, + renderExpirationTime + ); + } + + // Store the observedBits on the fiber's stateNode for quick access. + var observedBits = newProps.observedBits; + if (observedBits === undefined || observedBits === null) { + // Subscribe to all changes by default + observedBits = MAX_SIGNED_31_BIT_INT; + } + workInProgress.stateNode = observedBits; + + var newChildren = newProps.render(newValue); + reconcileChildren(current, workInProgress, newChildren); + return workInProgress.child; + } else { + return null; + } + } + + /* + 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; + case ContextProvider: + pushProvider(workInProgress); + 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 moveCallToHandlerPhase( + 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); + case Mode: + return updateMode(current, workInProgress); + case ContextProvider: + return updateContextProvider( 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." + case ContextConsumer: + return updateContextConsumer( + current, + workInProgress, + renderExpirationTime ); - // eslint-disable-next-line no-fallthrough default: invariant( false, @@ -8915,212 +9428,191 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { } } - 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; + 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." + ); + } - var callComponentWillUnmountWithTimer = function(current, instance) { - startPhaseTimer(current, "componentWillUnmount"); - instance.props = current.memoizedProps; - instance.state = current.memoizedState; - instance.componentWillUnmount(); - stopPhaseTimer(); - }; + // Add an error effect so we can handle the error during the commit phase + workInProgress.effectTag |= Err; - // 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); - } + // 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; } - } - 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); - } - } + if ( + workInProgress.expirationTime === NoWork || + workInProgress.expirationTime > renderExpirationTime + ) { + return bailoutOnLowPriority(current, workInProgress); } - } - 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; + // 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; - // 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); - } + // Unmount the current children as if the component rendered null + var nextChildren = null; + reconcileChildrenAtExpirationTime( + current, + workInProgress, + nextChildren, + renderExpirationTime + ); - 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." - ); - } + if (workInProgress.tag === ClassComponent) { + var instance = workInProgress.stateNode; + workInProgress.memoizedProps = instance.props; + workInProgress.memoizedState = instance.state; } - } - 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); - } - } + return workInProgress.child; } - function commitDetachRef(current) { - var currentRef = current.ref; - if (currentRef !== null) { - currentRef(null); - } + 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; } - // 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); - } + function markRef(workInProgress) { + workInProgress.effectTag |= Ref; + } - 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; + 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.pendingProps.value); + } else if (node.child !== null) { + node.child["return"] = node; + node = node.child; + continue; } - 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); + while (node.sibling === null) { + if (node["return"] === null || node["return"] === workInProgress) { + return; } - return; + node = node["return"]; } + node.sibling["return"] = node["return"]; + node = node.sibling; } } - 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) - ) { + function moveCallToHandlerPhase( + current, + workInProgress, + renderExpirationTime + ) { + var props = workInProgress.memoizedProps; + invariant( + props, + "Should be resolved by now. This error is likely caused by a bug in " + + "React. Please file an issue." + ); + + // 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 = props.handler; + var childProps = props.props; + var nextChildren = fn(childProps, 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 === root) { + if (node === workInProgress) { return; } while (node.sibling === null) { - if (node["return"] === null || node["return"] === root) { + if (node["return"] === null || node["return"] === workInProgress) { return; } node = node["return"]; @@ -9130,379 +9622,532 @@ var ReactFiberCommitWork = function(config, captureError) { } } - 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; + 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, + currentHostContext + ) { + // 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."); } - } - - if (!mutation) { - var commitContainer = void 0; - if (persistence) { - var replaceContainerChildren = persistence.replaceContainerChildren, - createContainerChildSet = persistence.createContainerChildSet; + } else if (persistence) { + if (enablePersistentReconciler) { + // Persistent host tree mode + var cloneInstance = persistence.cloneInstance, + createContainerChildSet = persistence.createContainerChildSet, + appendChildToContainerChildSet = + persistence.appendChildToContainerChildSet, + finalizeContainerChildren = persistence.finalizeContainerChildren; - var emptyPortalContainer = function(current) { - var portal = current.stateNode; - var containerInfo = portal.containerInfo; + // An unfortunate fork of appendAllChildren because we have two different parent types. - var emptyChildSet = createContainerChildSet(containerInfo); - replaceContainerChildren(containerInfo, emptyChildSet); - }; - commitContainer = function(finishedWork) { - switch (finishedWork.tag) { - case ClassComponent: { - return; + 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; } - case HostComponent: { + if (node === workInProgress) { return; } - case HostText: { - return; + while (node.sibling === null) { + if (node["return"] === null || node["return"] === workInProgress) { + return; + } + node = node["return"]; } - case HostRoot: - case HostPortal: { - var portalOrRoot = finishedWork.stateNode; - var containerInfo = portalOrRoot.containerInfo, - _pendingChildren = portalOrRoot.pendingChildren; - - replaceContainerChildren(containerInfo, _pendingChildren); - 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, + currentHostContext + ) { + // 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, + currentHostContext + ) + ) { + markUpdate(workInProgress); } - 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." - ); + 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 { - commitContainer = function(finishedWork) { + invariant(false, "Persistent reconciler is disabled."); + } + } else { + if (enableNoopReconciler) { + // No host operations + updateHostContainer = function(workInProgress) { // 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 + updateHostComponent = function( + current, + workInProgress, + updatePayload, + type, + oldProps, + newProps, + rootContainerInstance, + currentHostContext + ) { + // Noop + }; + updateHostText = function(current, workInProgress, oldText, newText) { + // Noop }; - } 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; + function completeWork(current, workInProgress, renderExpirationTime) { + var newProps = workInProgress.pendingProps; + switch (workInProgress.tag) { + case FunctionalComponent: + return null; + case ClassComponent: { + // We are leaving this subtree, so pop context if any. + popContextProvider(workInProgress); + return null; } - 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 - ); - } + case HostRoot: { + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + var fiberRoot = workInProgress.stateNode; + if (fiberRoot.pendingContext) { + fiberRoot.context = fiberRoot.pendingContext; + fiberRoot.pendingContext = null; + } - 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; + 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; } - node = node["return"]; + updateHostContainer(workInProgress); + return null; } - 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; + 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, + currentHostContext + ); + + if (current.ref !== workInProgress.ref) { + markRef(workInProgress); + } + } else { + if (!newProps) { + invariant( + workInProgress.stateNode !== null, + "We must have new props for new mounts. This error is likely " + + "caused by a bug in React. Please file an issue." + ); + // 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, + _currentHostContext + ) + ) { + 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); + } } - // 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; + 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 { - node.child["return"] = node; - node = node.child; + if (typeof newText !== "string") { + invariant( + workInProgress.stateNode !== null, + "We must have new props for new mounts. This error is likely " + + "caused by a bug in React. Please file an issue." + ); + // 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; } - // 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 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 Mode: + return null; case HostPortal: - parent = parentFiber.stateNode.containerInfo; - isContainer = true; - break; + popHostContainer(workInProgress); + updateHostContainer(workInProgress); + return null; + case ContextProvider: + // Pop provider fiber + popProvider(workInProgress); + return null; + case ContextConsumer: + 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, - "Invalid host parent fiber. This error is likely caused by a bug " + - "in React. Please file an issue." + "Unknown unit of work tag. 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; + return { + completeWork: completeWork + }; +}; - // Each iteration, currentParent is populated with node's host parent if not - // currentParentIsValid. - var currentParentIsValid = false; - var currentParent = void 0; - var currentParentIsContainer = void 0; +var invokeGuardedCallback$3 = ReactErrorUtils.invokeGuardedCallback; +var hasCaughtError$1 = ReactErrorUtils.hasCaughtError; +var clearCaughtError$1 = ReactErrorUtils.clearCaughtError; - while (true) { - if (!currentParentIsValid) { - var parent = node["return"]; - findParent: while (true) { - invariant( - parent !== null, - "Expected to find a host parent. This error is likely caused by " + - "a bug in React. Please file an issue." - ); - 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; - } +var ReactFiberCommitWork = function(config, captureError) { + var getPublicInstance = config.getPublicInstance, + mutation = config.mutation, + persistence = config.persistence; - 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; + 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$3( + null, + callComponentWillUnmountWithTimer, + null, + current, + instance + ); + if (hasCaughtError$1()) { + var unmountError = clearCaughtError$1(); + captureError(current, unmountError); } - 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; + } + } + + function safelyDetachRef(current) { + var ref = current.ref; + if (ref !== null) { + { + invokeGuardedCallback$3(null, ref, null, null); + if (hasCaughtError$1()) { + var refError = clearCaughtError$1(); + captureError(current, refError); } - } - 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) { + 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 = null; + if (finishedWork.child !== null) { + switch (finishedWork.child.tag) { + case HostComponent: + _instance = getPublicInstance(finishedWork.child.stateNode); + break; + case ClassComponent: + _instance = finishedWork.child.stateNode; + break; + } + } + commitCallbacks(_updateQueue, _instance); + } 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 _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; - // 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 - ); - } + var props = finishedWork.memoizedProps; + commitMount(_instance2, type, props, finishedWork); } + return; } case HostText: { - invariant( - finishedWork.stateNode !== null, - "This should have a text node initialized. This error is likely " + - "caused by a bug in React. Please file an issue." - ); - 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); + // We have no life-cycles associated with text. return; } - case HostRoot: { + case HostPortal: { + // We have no life-cycles associated with portals. return; } default: { @@ -9515,3240 +10160,3360 @@ var ReactFiberCommitWork = function(config, captureError) { } } - 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) { - invariant( - c !== NO_CONTEXT, - "Expected host context to exist. This error is likely caused by a bug " + - "in React. Please file an issue." - ); - 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 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 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; + function commitDetachRef(current) { + var currentRef = current.ref; + if (currentRef !== null) { + currentRef(null); } - - // 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; + // 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); } - pop(contextStackCursor, fiber); - pop(contextFiberStackCursor, fiber); + 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 resetHostContainer() { - contextStackCursor.current = NO_CONTEXT; - rootInstanceStackCursor.current = NO_CONTEXT; + 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; + } } - 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; - } - }; + 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; + } } - 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; + var emptyPortalContainer = void 0; - // The deepest Fiber on the stack involved in a hydration context. - // This may have been an insertion or a hydration. + if (!mutation) { + var commitContainer = void 0; + if (persistence) { + var replaceContainerChildren = persistence.replaceContainerChildren, + createContainerChildSet = persistence.createContainerChildSet; - var hydrationParentFiber = null; - var nextHydratableInstance = null; - var isHydrating = false; + emptyPortalContainer = function(current) { + var portal = current.stateNode; + var containerInfo = portal.containerInfo; - function enterHydrationState(fiber) { - var parentInstance = fiber.stateNode.containerInfo; - nextHydratableInstance = getFirstHydratableChild(parentInstance); - hydrationParentFiber = fiber; - isHydrating = true; - return true; - } + 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; - 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; - } + 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); + }, - 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; + commitLifeCycles: commitLifeCycles, + commitAttachRef: commitAttachRef, + commitDetachRef: commitDetachRef + }; + } else if (persistence) { + invariant(false, "Persistent reconciler is disabled."); } else { - returnFiber.firstEffect = returnFiber.lastEffect = childToDelete; + 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 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 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 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; + 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; } - return false; + node = node["return"]; } - case HostText: { - var text = fiber.pendingProps; - var textInstance = canHydrateTextInstance(nextInstance, text); - if (textInstance !== null) { - fiber.stateNode = textInstance; - return true; + 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; } - return false; } - default: - return false; + // Check if this host node is stable or about to be placed. + if (!(node.effectTag & Placement)) { + // Found it! + return node.stateNode; + } } } - function tryToClaimNextHydratableInstance(fiber) { - if (!isHydrating) { - return; + 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." + ); } - var nextInstance = nextHydratableInstance; - if (!nextInstance) { - // Nothing to hydrate. Make it an insertion. - insertNonHydratedInstance(hydrationParentFiber, fiber); - isHydrating = false; - hydrationParentFiber = fiber; - return; + 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; } - 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; + + 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; } - // 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); + while (node.sibling === null) { + if (node["return"] === null || node["return"] === finishedWork) { + return; + } + node = node["return"]; + } + node.sibling["return"] = node["return"]; + node = node.sibling; } - 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 unmountHostComponents(current) { + // We only have the top Fiber that was inserted but we need recurse down its + var node = current; - 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; - } + // 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) { + invariant( + parent !== null, + "Expected to find a host parent. This error is likely caused by " + + "a bug in React. Please file an issue." + ); + 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; } } - } - 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); + 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; } - - popToNextHostParent(fiber); - nextHydratableInstance = hydrationParentFiber - ? getNextHydratableSibling(fiber.stateNode) - : null; - return true; } - function resetHydrationState() { - hydrationParentFiber = null; - nextHydratableInstance = null; - isHydrating = false; + function commitDeletion(current) { + // Recursively delete all host nodes from the parent. + // Detach refs and call componentWillUnmount() on the whole subtree. + unmountHostComponents(current); + detachFiber(current); } - 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 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; + 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 + ); + } } - warning( - false, - "setState(...): Cannot call setState() inside getChildContext()" + return; + } + case HostText: { + invariant( + finishedWork.stateNode !== null, + "This should have a text node initialized. This error is likely " + + "caused by a bug in React. Please file an issue." ); - didWarnSetStateChildContext = true; - break; - case "render": - if (didWarnAboutStateTransition) { - return; - } - warning( + 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, - "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`." + "This unit of work tag should not have side-effects. This error is " + + "likely caused by a bug in React. Please file an issue." ); - 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); + function commitResetTextContent(current) { + resetTextContent(current.stateNode); + } - // Used to ensure computeUniqueAsyncExpiration is monotonically increases. - var lastUniqueAsyncExpiration = 0; + if (enableMutatingReconciler) { + return { + commitResetTextContent: commitResetTextContent, + commitPlacement: commitPlacement, + commitDeletion: commitDeletion, + commitWork: commitWork, + commitLifeCycles: commitLifeCycles, + commitAttachRef: commitAttachRef, + commitDetachRef: commitDetachRef + }; + } else { + invariant(false, "Mutating reconciler is disabled."); + } +}; - // 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 NO_CONTEXT = {}; - var isWorking = false; +var ReactFiberHostContext = function(config) { + var getChildHostContext = config.getChildHostContext, + getRootHostContext = config.getRootHostContext; - // 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; + var contextStackCursor = createCursor(NO_CONTEXT); + var contextFiberStackCursor = createCursor(NO_CONTEXT); + var rootInstanceStackCursor = createCursor(NO_CONTEXT); - // The next fiber with an effect that we're currently committing. - var nextEffect = null; + function requiredContext(c) { + invariant( + c !== NO_CONTEXT, + "Expected host context to exist. This error is likely caused by a bug " + + "in React. Please file an issue." + ); + return c; + } - // 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; + function getRootHostContainer() { + var rootInstance = requiredContext(rootInstanceStackCursor.current); + return rootInstance; + } - var isCommitting = false; - var isUnmounting = false; + 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); - // Used for performance tracking. - var interruptedBy = null; + var nextRootContext = getRootHostContext(nextRootInstance); - function resetContextStack() { - // Reset the stack - reset(); - // Reset the cursors - resetContext(); - resetHostContainer(); + // 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 commitAllHostEffects() { - while (nextEffect !== null) { - { - ReactDebugCurrentFiber.setCurrentFiber(nextEffect); - } - recordEffect(); - - var effectTag = nextEffect.effectTag; - if (effectTag & ContentReset) { - commitResetTextContent(nextEffect); - } + function popHostContainer(fiber) { + pop(contextStackCursor, fiber); + pop(contextFiberStackCursor, fiber); + pop(rootInstanceStackCursor, fiber); + } - if (effectTag & Ref) { - var current = nextEffect.alternate; - if (current !== null) { - commitDetachRef(current); - } - } + function getHostContext() { + var context = requiredContext(contextStackCursor.current); + return context; + } - // 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; + function pushHostContext(fiber) { + var rootInstance = requiredContext(rootInstanceStackCursor.current); + var context = requiredContext(contextStackCursor.current); + var nextContext = getChildHostContext(context, fiber.type, rootInstance); - // 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; + // Don't push this Fiber's context unless it's unique. + if (context === nextContext) { + return; } - { - ReactDebugCurrentFiber.resetCurrentFiber(); + // 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 commitAllLifeCycles() { - while (nextEffect !== null) { - var effectTag = nextEffect.effectTag; + function resetHostContainer() { + contextStackCursor.current = NO_CONTEXT; + rootInstanceStackCursor.current = NO_CONTEXT; + } - if (effectTag & (Update | Callback)) { - recordEffect(); - var current = nextEffect.alternate; - commitLifeCycles(current, nextEffect); - } + return { + getHostContext: getHostContext, + getRootHostContainer: getRootHostContainer, + popHostContainer: popHostContainer, + popHostContext: popHostContext, + pushHostContainer: pushHostContainer, + pushHostContext: pushHostContext, + resetHostContainer: resetHostContainer + }; +}; - if (effectTag & Ref) { - recordEffect(); - commitAttachRef(nextEffect); - } +var ReactFiberHydrationContext = function(config) { + var shouldSetTextContent = config.shouldSetTextContent, + hydration = config.hydration; - if (effectTag & Err) { - recordEffect(); - commitErrorHandling(nextEffect); - } + // If this doesn't have hydration mode. - 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; - } + 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; + } + }; } - 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 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 root = finishedWork.stateNode; - invariant( - root.current !== finishedWork, - "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." - ); - root.isReadyForCommit = false; + var hydrationParentFiber = null; + var nextHydratableInstance = null; + var isHydrating = false; - // Reset this to null before calling lifecycles - ReactCurrentOwner.current = null; + function enterHydrationState(fiber) { + var parentInstance = fiber.stateNode.containerInfo; + nextHydratableInstance = getFirstHydratableChild(parentInstance); + hydrationParentFiber = fiber; + isHydrating = true; + return true; + } - 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; + 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; } - } else { - // There is no effect on the root. - firstEffect = finishedWork.firstEffect; } - prepareForCommit(); + var childToDelete = createFiberFromHostInstanceForDeletion(); + childToDelete.stateNode = instance; + childToDelete["return"] = returnFiber; + childToDelete.effectTag = Deletion; - // 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(); + // 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; } - } - if (didError) { - invariant( - nextEffect !== null, - "Should have next effect. This error is likely caused by a bug " + - "in React. Please file an issue." - ); - captureError(nextEffect, _error); - // Clean-up - if (nextEffect !== null) { - nextEffect = nextEffect.nextEffect; + 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; } } - 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(); + 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; } - if (_didError) { - invariant( - nextEffect !== null, - "Should have next effect. This error is likely caused by a bug " + - "in React. Please file an issue." - ); - captureError(nextEffect, _error2); - if (nextEffect !== null) { - nextEffect = nextEffect.nextEffect; + case HostText: { + var text = fiber.pendingProps; + var textInstance = canHydrateTextInstance(nextInstance, text); + if (textInstance !== null) { + fiber.stateNode = textInstance; + return true; } + return false; } + default: + return false; } + } - 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; + function tryToClaimNextHydratableInstance(fiber) { + if (!isHydrating) { + return; } - - if (firstUncaughtError !== null) { - var _error3 = firstUncaughtError; - firstUncaughtError = null; - onUncaughtError(_error3); + var nextInstance = nextHydratableInstance; + if (!nextInstance) { + // Nothing to hydrate. Make it an insertion. + insertNonHydratedInstance(hydrationParentFiber, fiber); + isHydrating = false; + hydrationParentFiber = fiber; + return; } - - var remainingTime = root.current.expirationTime; - - if (remainingTime === NoWork) { - capturedErrors = null; - failedBoundaries = null; + 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); } - - return remainingTime; + hydrationParentFiber = fiber; + nextHydratableInstance = getFirstHydratableChild(nextInstance); } - 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; + 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; } - workInProgress.expirationTime = newExpirationTime; + return false; } - 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); + 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; + } + } } - // If completing this work spawned new work, do that next. We'll come - // back here again. - return next; } + } + return shouldUpdate; + } - 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; - } + function popToNextHostParent(fiber) { + var parent = fiber["return"]; + while ( + parent !== null && + parent.tag !== HostComponent && + parent.tag !== HostRoot + ) { + parent = parent["return"]; + } + hydrationParentFiber = parent; + } - // 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; - } - } + 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; + } - stopWorkTimer(workInProgress); - if (true && ReactFiberInstrumentation_1.debugTool) { - ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); - } + var type = fiber.type; - 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; + // 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); } } - // 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; + popToNextHostParent(fiber); + nextHydratableInstance = hydrationParentFiber + ? getNextHydratableSibling(fiber.stateNode) + : null; + return true; } - 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; + function resetHydrationState() { + hydrationParentFiber = null; + nextHydratableInstance = null; + isHydrating = false; + } - // See if beginning this work spawns more work. - startWorkTimer(workInProgress); - { - ReactDebugCurrentFiber.setCurrentFiber(workInProgress); - } + return { + enterHydrationState: enterHydrationState, + resetHydrationState: resetHydrationState, + tryToClaimNextHydratableInstance: tryToClaimNextHydratableInstance, + prepareToHydrateHostInstance: prepareToHydrateHostInstance, + prepareToHydrateHostTextInstance: prepareToHydrateHostTextInstance, + popHydrationState: popHydrationState + }; +}; - var next = beginWork(current, workInProgress, nextRenderExpirationTime); - { - ReactDebugCurrentFiber.resetCurrentFiber(); - } - if (true && ReactFiberInstrumentation_1.debugTool) { - ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); - } +// 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 +}; - if (next === null) { - // If this doesn't spawn new work, complete the current work. - next = completeUnitOfWork(workInProgress); - } +var ReactFiberInstrumentation_1 = ReactFiberInstrumentation; - ReactCurrentOwner.current = null; +// Module provided by RN: +/** + * Intercept lifecycle errors and ensure they are shown with the correct stack + * trace within the native redbox component. + */ +function showErrorDialog(capturedError) { + var componentStack = capturedError.componentStack, + error = capturedError.error; - return next; - } + var errorToHandle = void 0; - 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; + // Typically Errors are thrown but eg strings or null can be thrown as well. + if (error instanceof Error) { + var message = error.message, + name = error.name; - // See if beginning this work spawns more work. - startWorkTimer(workInProgress); - { - ReactDebugCurrentFiber.setCurrentFiber(workInProgress); - } - var next = beginFailedWork( - current, - workInProgress, - nextRenderExpirationTime + var summary = message ? name + ": " + message : name; + + errorToHandle = error; + + try { + errorToHandle.message = + summary + "\n\nThis error is located at:" + componentStack; + } catch (e) {} + } else if (typeof error === "string") { + errorToHandle = new Error( + error + "\n\nThis error is located at:" + componentStack ); - { - ReactDebugCurrentFiber.resetCurrentFiber(); - } - if (true && ReactFiberInstrumentation_1.debugTool) { - ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); - } + } else { + errorToHandle = new Error("Unspecified error at:" + componentStack); + } - if (next === null) { - // If this doesn't spawn new work, complete the current work. - next = completeUnitOfWork(workInProgress); - } + ExceptionsManager.handleException(errorToHandle, false); - ReactCurrentOwner.current = null; + // Return false here to prevent ReactFiberErrorLogger default behavior of + // logging error details to console.error. Calls to console.error are + // automatically routed to the native redbox controller, which we've already + // done above by calling ExceptionsManager. + return false; +} - return next; - } +function logCapturedError(capturedError) { + var logError = showErrorDialog(capturedError); - 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; - } + // Allow injected showErrorDialog() to prevent default console.error logging. + // This enables renderers like ReactNative to better manage redbox behavior. + if (logError === false) { + 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); - } - } + var error = capturedError.error; + var suppressLogging = error && error.suppressReactErrorLogging; + if (suppressLogging) { + return; } - function slowWorkLoopThatChecksForFailedWork(expirationTime) { - if ( - nextRenderExpirationTime === NoWork || - nextRenderExpirationTime > expirationTime - ) { - return; - } + { + var componentName = capturedError.componentName, + componentStack = capturedError.componentStack, + errorBoundaryName = capturedError.errorBoundaryName, + errorBoundaryFound = capturedError.errorBoundaryFound, + willRetry = capturedError.willRetry; - 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); - } + 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 { - // 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); - } - } + 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); } +} - 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); +var invokeGuardedCallback$2 = ReactErrorUtils.invokeGuardedCallback; +var hasCaughtError = ReactErrorUtils.hasCaughtError; +var clearCaughtError = ReactErrorUtils.clearCaughtError; - // 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); +var didWarnAboutStateTransition = void 0; +var didWarnSetStateChildContext = void 0; +var warnAboutUpdateOnUnmounted = void 0; +var warnAboutInvalidUpdates = void 0; - // Continue working. - workLoop(expirationTime); - } +{ + didWarnAboutStateTransition = false; + didWarnSetStateChildContext = false; + var didWarnStateUpdateForUnmountedComponent = {}; - function renderRoot(root, expirationTime) { - invariant( - !isWorking, - "renderRoot was called recursively. This error is likely caused " + - "by a bug in React. Please file an issue." + 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 ); - 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; + didWarnStateUpdateForUnmountedComponent[componentName] = true; + }; - // 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 - ); + 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; - startWorkLoopTimer(nextUnitOfWork); + var _ReactFiberBeginWork = ReactFiberBeginWork( + config, + hostContext, + hydrationContext, + scheduleWork, + computeExpirationForFiber + ), + beginWork = _ReactFiberBeginWork.beginWork, + beginFailedWork = _ReactFiberBeginWork.beginFailedWork; - var didError = false; - var error = null; - { - invokeGuardedCallback$1(null, workLoop, null, expirationTime); - if (hasCaughtError()) { - didError = true; - error = clearCaughtError(); - } - } + var _ReactFiberCompleteWo = ReactFiberCompleteWork( + config, + hostContext, + hydrationContext + ), + completeWork = _ReactFiberCompleteWo.completeWork; - // 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 _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 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; - } + var now = config.now, + scheduleDeferredCallback = config.scheduleDeferredCallback, + cancelDeferredCallback = config.cancelDeferredCallback, + prepareForCommit = config.prepareForCommit, + resetAfterCommit = config.resetAfterCommit; - // "Capture" the error by finding the nearest boundary. If there is no - // error boundary, we use the root. - var boundary = captureError(failedWork, error); - invariant( - boundary !== null, - "Should have found an error boundary. This error is likely " + - "caused by a bug in React. Please file an issue." - ); + // Represents the current time in ms. - if (didFatal) { - // The error we just captured was a fatal error. This happens - // when the error propagates to the root more than once. - continue; - } + var startTime = now(); + var mostRecentCurrentTime = msToExpirationTime(0); - 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; - } + // Used to ensure computeUniqueAsyncExpiration is monotonically increases. + var lastUniqueAsyncExpiration = 0; - var uncaughtError = firstUncaughtError; + // 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; - // We're done performing work. Time to clean up. - stopWorkLoopTimer(interruptedBy); - interruptedBy = null; - isWorking = false; - didFatal = false; - firstUncaughtError = null; + var isWorking = false; - if (uncaughtError !== null) { - onUncaughtError(uncaughtError); - } + // 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; - return root.isReadyForCommit ? root.current.alternate : null; - } + // The next fiber with an effect that we're currently committing. + var nextEffect = 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(); - } + // 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; - // Search for the nearest error boundary. - var boundary = null; + var isCommitting = false; + var isUnmounting = false; - // Passed to logCapturedError() - var errorBoundaryFound = false; - var willRetry = false; - var errorBoundaryName = null; + // Used for performance tracking. + var interruptedBy = 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; + function resetContextStack() { + // Reset the stack + reset(); + // Reset the cursors + resetContext(); + resetProviderStack(); + resetHostContainer(); + } - 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; + function commitAllHostEffects() { + while (nextEffect !== null) { + { + ReactDebugCurrentFiber.setCurrentFiber(nextEffect); } - } 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; - } + recordEffect(); - if (isFailedBoundary(node)) { - // This boundary is already in a failed state. + var effectTag = nextEffect.effectTag; + if (effectTag & ContentReset) { + commitResetTextContent(nextEffect); + } - // 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 (effectTag & Ref) { + var current = nextEffect.alternate; + if (current !== null) { + commitDetachRef(current); + } + } - // 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 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; - // The error should propagate to the next boundary -— we keep looking. - boundary = null; - willRetry = false; + // 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; } - - node = node["return"]; } + nextEffect = nextEffect.nextEffect; } - 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); + { + ReactDebugCurrentFiber.resetCurrentFiber(); + } + } - // 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); + function commitAllLifeCycles() { + { + ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings(); - // 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(); + if (warnAboutDeprecatedLifecycles) { + ReactStrictModeWarnings.flushPendingDeprecationWarnings(); } + } - var capturedError = { - componentName: _componentName, - componentStack: _componentStack, - error: error, - errorBoundary: errorBoundaryFound ? boundary.stateNode : null, - errorBoundaryFound: errorBoundaryFound, - errorBoundaryName: errorBoundaryName, - willRetry: willRetry - }; + while (nextEffect !== null) { + var effectTag = nextEffect.effectTag; - capturedErrors.set(boundary, capturedError); + if (effectTag & (Update | Callback)) { + recordEffect(); + var current = nextEffect.alternate; + commitLifeCycles(current, nextEffect); + } - 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 (effectTag & Ref) { + recordEffect(); + commitAttachRef(nextEffect); } - // 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); + if (effectTag & Err) { + recordEffect(); + commitErrorHandling(nextEffect); } - return boundary; - } else if (firstUncaughtError === null) { - // If no boundary is found, we'll need to throw the error - firstUncaughtError = error; + + 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; } - 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 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(); - 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))) + var root = finishedWork.stateNode; + invariant( + root.current !== finishedWork, + "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." ); - } + root.isReadyForCommit = false; - 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); - } + // 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; } - invariant( - capturedError != null, - "No error for given unit of work. This error is likely caused by a " + - "bug in React. Please file an issue." - ); - - switch (effectfulFiber.tag) { - case ClassComponent: - var instance = effectfulFiber.stateNode; - - var info = { - componentStack: capturedError.componentStack - }; + prepareForCommit(); - // 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; + // 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$2(null, commitAllHostEffects, null); + if (hasCaughtError()) { + didError = true; + _error = clearCaughtError(); } - return; - default: + } + if (didError) { invariant( - false, - "Invalid type of work. This error is likely caused by a bug in " + - "React. Please file an issue." + nextEffect !== null, + "Should have next effect. This error is likely caused by a bug " + + "in React. Please file an issue." ); + captureError(nextEffect, _error); + // Clean-up + if (nextEffect !== null) { + nextEffect = nextEffect.nextEffect; + } + } } - } + stopCommitHostEffectsTimer(); - 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; + 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$2(null, commitAllLifeCycles, null); + if (hasCaughtError()) { + _didError = true; + _error2 = clearCaughtError(); + } } - if (node === to || node.alternate === to) { - stopFailedWorkTimer(node); - break; - } else { - stopWorkTimer(node); + if (_didError) { + invariant( + nextEffect !== null, + "Should have next effect. This error is likely caused by a bug " + + "in React. Please file an issue." + ); + captureError(nextEffect, _error2); + if (nextEffect !== null) { + nextEffect = nextEffect.nextEffect; + } } - 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); - } + 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; + } - // Creates a unique async expiration time. - function computeUniqueAsyncExpiration() { - var result = computeAsyncExpiration(); - if (result <= lastUniqueAsyncExpiration) { - // Since we assume the current time monotonically increases, we only hit - // this branch when computeUniqueAsyncExpiration is fired multiple times - // within a 200ms window (or whatever the async bucket size is). - result = lastUniqueAsyncExpiration + 1; + if (firstUncaughtError !== null) { + var _error3 = firstUncaughtError; + firstUncaughtError = null; + onUncaughtError(_error3); } - lastUniqueAsyncExpiration = result; - return lastUniqueAsyncExpiration; - } - 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(); - } + var remainingTime = root.current.expirationTime; + + if (remainingTime === NoWork) { + capturedErrors = null; + failedBoundaries = null; } - return expirationTime; - } - function scheduleWork(fiber, expirationTime) { - return scheduleWorkImpl(fiber, expirationTime, false); + return remainingTime; } - 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 resetExpirationTime(workInProgress, renderTime) { + if (renderTime !== Never && workInProgress.expirationTime === Never) { + // The children of this component are hidden. Don't bubble their + // expiration times. + return; } - } - function scheduleWorkImpl(fiber, expirationTime, isErrorRecovery) { - recordScheduleUpdate(); + // Check for pending updates. + var newExpirationTime = getUpdateExpirationTime(workInProgress); - { - if (!isErrorRecovery && fiber.tag === ClassComponent) { - var instance = fiber.stateNode; - warnAboutInvalidUpdates(instance); - } - } + // TODO: Calls need to visit stateNode - var node = fiber; - while (node !== null) { - // Walk the parent path to the root and update each node's - // expiration time. + // Bubble up the earliest expiration time. + var child = workInProgress.child; + while (child !== null) { if ( - node.expirationTime === NoWork || - node.expirationTime > expirationTime + child.expirationTime !== NoWork && + (newExpirationTime === NoWork || + newExpirationTime > child.expirationTime) ) { - node.expirationTime = expirationTime; + newExpirationTime = child.expirationTime; } - if (node.alternate !== null) { - if ( - node.alternate.expirationTime === NoWork || - node.alternate.expirationTime > expirationTime - ) { - node.alternate.expirationTime = 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 (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); - } + 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; } - return; + returnFiber.lastEffect = workInProgress.lastEffect; } - } - node = node["return"]; - } - } - function scheduleErrorRecovery(fiber) { - scheduleWorkImpl(fiber, Sync, true); - } + // 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; + } + } - function recalculateCurrentTime() { - // Subtract initial time so it fits inside 32bits - var ms = now() - startTime; - mostRecentCurrentTime = msToExpirationTime(ms); - return mostRecentCurrentTime; - } + stopWorkTimer(workInProgress); + if (true && ReactFiberInstrumentation_1.debugTool) { + ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); + } - function deferredUpdates(fn) { - var previousExpirationContext = expirationContext; - expirationContext = computeAsyncExpiration(); - try { - return fn(); - } finally { - expirationContext = previousExpirationContext; + 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 syncUpdates(fn) { - var previousExpirationContext = expirationContext; - expirationContext = Sync; - try { - return fn(); - } finally { - expirationContext = previousExpirationContext; + 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); } - } - // TODO: Everything below this is written as if it has been lifted to the - // renderers. I'll do this in a follow-up. + var next = beginWork(current, workInProgress, nextRenderExpirationTime); + { + ReactDebugCurrentFiber.resetCurrentFiber(); + } + if (true && ReactFiberInstrumentation_1.debugTool) { + ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); + } - // Linked-list of roots - var firstScheduledRoot = null; - var lastScheduledRoot = null; + if (next === null) { + // If this doesn't spawn new work, complete the current work. + next = completeUnitOfWork(workInProgress); + } - 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; + ReactCurrentOwner.current = null; - var isBatchingUpdates = false; - var isUnbatchingUpdates = false; + return next; + } - var completedBatches = null; + function performFailedUnitOfWork(workInProgress) { + { + ReactStrictModeWarnings.discardPendingWarnings(); + } - // Use these to prevent an infinite loop of nested updates - var NESTED_UPDATE_LIMIT = 1000; - var nestedUpdateCount = 0; + // 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; - var timeHeuristicForUnitOfWork = 1; + // 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); + } - 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(); + if (next === null) { + // If this doesn't spawn new work, complete the current work. + next = completeUnitOfWork(workInProgress); } - // Compute a timeout for the given expiration time. - var currentMs = now() - startTime; - var expirationMs = expirationTimeToMs(expirationTime); - var timeout = expirationMs - currentMs; + ReactCurrentOwner.current = null; - callbackExpirationTime = expirationTime; - callbackID = scheduleDeferredCallback(performAsyncWork, { - timeout: timeout - }); + return next; } - // 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." - ); + 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; } - // 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; + if (nextRenderExpirationTime <= mostRecentCurrentTime) { + // Flush all expired work. + while (nextUnitOfWork !== null) { + nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } 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; + // Flush asynchronous work until the deadline runs out of time. + while (nextUnitOfWork !== null && !shouldYield()) { + nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } + } - if (isRendering) { - // Prevent reentrancy. Remaining work will be scheduled at the end of - // the currently rendering batch. + function slowWorkLoopThatChecksForFailedWork(expirationTime) { + if ( + nextRenderExpirationTime === NoWork || + nextRenderExpirationTime > expirationTime + ) { 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(root, Sync, recalculateCurrentTime()); + 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); + } } - 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. - invariant( - previousScheduledRoot !== null && lastScheduledRoot !== null, - "Should have a previous and last root. This error is likely " + - "caused by a bug in React. Please file an issue." - ); - 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; + // 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 { - 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; + 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); - // 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; + // Continue working. + workLoop(expirationTime); + } + + function renderRoot(root, expirationTime) { + invariant( + !isWorking, + "renderRoot was called recursively. This error is likely caused " + + "by a bug in React. Please file an issue." + ); + 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 ( - previousFlushedRoot !== null && - previousFlushedRoot === highestPriorityRoot + root !== nextRoot || + expirationTime !== nextRenderExpirationTime || + nextUnitOfWork === null ) { - nestedUpdateCount++; - } else { - // Reset whenever we switch roots. - nestedUpdateCount = 0; + // Reset the stack and start working from the root. + resetContextStack(); + nextRoot = root; + nextRenderExpirationTime = expirationTime; + nextUnitOfWork = createWorkInProgress( + nextRoot.current, + null, + expirationTime + ); } - nextFlushedRoot = highestPriorityRoot; - nextFlushedExpirationTime = highestPriorityWork; - } - function performAsyncWork(dl) { - performWork(NoWork, dl); - } + startWorkLoopTimer(nextUnitOfWork); - function performWork(minExpirationTime, dl) { - deadline = dl; + var didError = false; + var error = null; + { + invokeGuardedCallback$2(null, workLoop, null, expirationTime); + if (hasCaughtError()) { + didError = true; + error = clearCaughtError(); + } + } - // Keep working on roots until there's no more work, or until the we reach - // the deadline. - findHighestPriorityRoot(); + // 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; + } - if (enableUserTimingAPI && deadline !== null) { - var didExpire = nextFlushedExpirationTime < recalculateCurrentTime(); - stopRequestCallbackTimer(didExpire); - } + 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; + } - while ( - nextFlushedRoot !== null && - nextFlushedExpirationTime !== NoWork && - (minExpirationTime === NoWork || - nextFlushedExpirationTime <= minExpirationTime) && - !deadlineDidExpire - ) { - performWorkOnRoot( - nextFlushedRoot, - nextFlushedExpirationTime, - recalculateCurrentTime() + // "Capture" the error by finding the nearest boundary. If there is no + // error boundary, we use the root. + var boundary = captureError(failedWork, error); + invariant( + boundary !== null, + "Should have found an error boundary. This error is likely " + + "caused by a bug in React. Please file an issue." ); - // Find the next highest priority work. - findHighestPriorityRoot(); + + 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$2( + null, + renderRootCatchBlock, + null, + root, + failedWork, + boundary, + expirationTime + ); + if (hasCaughtError()) { + didError = true; + error = clearCaughtError(); + continue; + } + } + // We're finished working. Exit the error loop. + break; } - // We're done flushing work. Either we ran out of time in this callback, - // or there's no more work left with sufficient priority. + var uncaughtError = firstUncaughtError; - // If we're inside a callback, set this to false since we just completed it. - if (deadline !== null) { - callbackExpirationTime = NoWork; - callbackID = -1; + // We're done performing work. Time to clean up. + stopWorkLoopTimer(interruptedBy); + interruptedBy = null; + isWorking = false; + didFatal = false; + firstUncaughtError = null; + + if (uncaughtError !== null) { + onUncaughtError(uncaughtError); } - // If there's work left over, schedule a new callback. - if (nextFlushedExpirationTime !== NoWork) { - scheduleCallbackWithExpiration(nextFlushedExpirationTime); + + 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(); } - // Clean-up. - deadline = null; - deadlineDidExpire = false; - nestedUpdateCount = 0; + // Search for the nearest error boundary. + var boundary = null; - finishRendering(); - } + // Passed to logCapturedError() + var errorBoundaryFound = false; + var willRetry = false; + var errorBoundaryName = null; - function flushRoot(root, expirationTime) { - invariant( - !isRendering, - "work.commit(): Cannot commit while already rendering. This likely " + - "means you attempted to commit from inside a lifecycle method." - ); - // Perform work on root as if the given expiration time is the current time. - // This has the effect of synchronously flushing all work up to and - // including the given time. - performWorkOnRoot(root, expirationTime, expirationTime); - finishRendering(); - } + // 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; - function finishRendering() { - if (completedBatches !== null) { - var batches = completedBatches; - completedBatches = null; - for (var i = 0; i < batches.length; i++) { - var batch = batches[i]; - try { - batch._onComplete(); - } catch (error) { - if (!hasUnhandledError) { - hasUnhandledError = true; - unhandledError = error; + 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 (hasUnhandledError) { - var _error4 = unhandledError; - unhandledError = null; - hasUnhandledError = false; - throw _error4; - } - } + 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(); + } - function performWorkOnRoot(root, expirationTime, currentTime) { - invariant( - !isRendering, - "performWorkOnRoot was called recursively. This error is likely caused " + - "by a bug in React. Please file an issue." - ); + var capturedError = { + componentName: _componentName, + componentStack: _componentStack, + error: error, + errorBoundary: errorBoundaryFound ? boundary.stateNode : null, + errorBoundaryFound: errorBoundaryFound, + errorBoundaryName: errorBoundaryName, + willRetry: willRetry + }; - isRendering = true; + capturedErrors.set(boundary, capturedError); - // Check if this is async work or sync/expired work. - if (expirationTime <= currentTime) { - // Flush sync work. - var finishedWork = root.finishedWork; - if (finishedWork !== null) { - // This root is already complete. We can commit it. - completeRoot(root, finishedWork, expirationTime); - } else { - root.finishedWork = null; - finishedWork = renderRoot(root, expirationTime); - if (finishedWork !== null) { - // We've completed the root. Commit it. - completeRoot(root, finishedWork, expirationTime); - } - } - } else { - // Flush async work. - var _finishedWork = root.finishedWork; - if (_finishedWork !== null) { - // This root is already complete. We can commit it. - completeRoot(root, _finishedWork, expirationTime); - } 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. - completeRoot(root, _finishedWork, expirationTime); - } else { - // There's no time left. Mark this root as complete. We'll come - // back and commit it later. - root.finishedWork = _finishedWork; - } + 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); } } - } - - isRendering = false; - } - function completeRoot(root, finishedWork, expirationTime) { - // Check if there's a batch that matches this expiration time. - var firstBatch = root.firstBatch; - if (firstBatch !== null && firstBatch._expirationTime <= expirationTime) { - if (completedBatches === null) { - completedBatches = [firstBatch]; + // 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 { - completedBatches.push(firstBatch); - } - if (firstBatch._defer) { - // This root is blocked from committing by a batch. Unschedule it until - // we receive another update. - root.finishedWork = finishedWork; - root.remainingExpirationTime = NoWork; - return; + // 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; } - - // Commit the root. - root.finishedWork = null; - root.remainingExpirationTime = commitRoot(finishedWork); + return null; } - // 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; + 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))) + ); } - // TODO: Not happy about this hook. Conceptually, renderRoot should return a - // tuple of (isReadyForCommit, didError, error) - function onUncaughtError(error) { - invariant( - nextFlushedRoot !== null, - "Should be working on a root. This error is likely caused by a bug in " + - "React. Please file an issue." + 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))) ); - // 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); + 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); + } } } - } - // 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; - } + invariant( + capturedError != null, + "No error for given unit of work. This error is likely caused by a " + + "bug in React. Please file an issue." + ); + + 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." + ); } - 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; - invariant( - !isRendering, - "flushSync was called from inside a lifecycle method. It cannot be " + - "called when React is already rendering." - ); - performWork(Sync, null); + 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; + case ContextProvider: + popProvider(node); + break; + } + if (node === to || node.alternate === to) { + stopFailedWorkTimer(node); + break; + } else { + stopWorkTimer(node); + } + node = node["return"]; } } - return { - computeAsyncExpiration: computeAsyncExpiration, - computeExpirationForFiber: computeExpirationForFiber, - scheduleWork: scheduleWork, - requestWork: requestWork, - flushRoot: flushRoot, - batchedUpdates: batchedUpdates, - unbatchedUpdates: unbatchedUpdates, - flushSync: flushSync, - deferredUpdates: deferredUpdates, - computeUniqueAsyncExpiration: computeUniqueAsyncExpiration - }; -}; - -{ - var didWarnAboutNestedUpdates = false; -} - -// 0 is PROD, 1 is DEV. -// Might add PROFILE later. - -function getContextForSubtree(parentComponent) { - if (!parentComponent) { - return emptyObject; + 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); } - 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, - computeUniqueAsyncExpiration = - _ReactFiberScheduler.computeUniqueAsyncExpiration, - computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber, - scheduleWork = _ReactFiberScheduler.scheduleWork, - requestWork = _ReactFiberScheduler.requestWork, - flushRoot = _ReactFiberScheduler.flushRoot, - batchedUpdates = _ReactFiberScheduler.batchedUpdates, - unbatchedUpdates = _ReactFiberScheduler.unbatchedUpdates, - flushSync = _ReactFiberScheduler.flushSync, - deferredUpdates = _ReactFiberScheduler.deferredUpdates; + // Creates a unique async expiration time. + function computeUniqueAsyncExpiration() { + var result = computeAsyncExpiration(); + if (result <= lastUniqueAsyncExpiration) { + // Since we assume the current time monotonically increases, we only hit + // this branch when computeUniqueAsyncExpiration is fired multiple times + // within a 200ms window (or whatever the async bucket size is). + result = lastUniqueAsyncExpiration + 1; + } + lastUniqueAsyncExpiration = result; + return lastUniqueAsyncExpiration; + } - function computeRootExpirationTime(current, element) { + function computeExpirationForFiber(fiber) { 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(); + 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 { - expirationTime = computeExpirationForFiber(current); + // No explicit expiration context was set, and we're not currently + // performing work. Calculate a new expiration time. + if (fiber.internalContextTag & AsyncUpdates) { + // This is an async update + expirationTime = computeAsyncExpiration(); + } else { + // This is a sync update + expirationTime = Sync; + } } return expirationTime; } - function scheduleRootUpdate(current, element, expirationTime, 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" - ); + 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(); - 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 - ); + if (!isErrorRecovery && fiber.tag === ClassComponent) { + var instance = fiber.stateNode; + warnAboutInvalidUpdates(instance); + } } - var update = { - expirationTime: expirationTime, - partialState: { element: element }, - callback: callback, - isReplace: false, - isForced: false, - next: null - }; - insertUpdateIntoFiber(current, update); - scheduleWork(current, expirationTime); - - return expirationTime; - } - - function updateContainerAtExpirationTime( - element, - container, - parentComponent, - expirationTime, - callback - ) { - // TODO: If this is a nested container, this won't be the root. - var current = container.current; + 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; - { - if (ReactFiberInstrumentation_1.debugTool) { - if (current.alternate === null) { - ReactFiberInstrumentation_1.debugTool.onMountContainer(container); - } else if (element === null) { - ReactFiberInstrumentation_1.debugTool.onUnmountContainer(container); + checkRootNeedsClearing(root, fiber, expirationTime); + requestWork(root, expirationTime); + checkRootNeedsClearing(root, fiber, expirationTime); } else { - ReactFiberInstrumentation_1.debugTool.onUpdateContainer(container); + { + if (!isErrorRecovery && fiber.tag === ClassComponent) { + warnAboutUpdateOnUnmounted(fiber); + } + } + return; } } + node = node["return"]; } + } - var context = getContextForSubtree(parentComponent); - if (container.context === null) { - container.context = context; - } else { - container.pendingContext = context; - } + function scheduleErrorRecovery(fiber) { + scheduleWorkImpl(fiber, Sync, true); + } - return scheduleRootUpdate(current, element, expirationTime, callback); + function recalculateCurrentTime() { + // Subtract initial time so it fits inside 32bits + var ms = now() - startTime; + mostRecentCurrentTime = msToExpirationTime(ms); + return mostRecentCurrentTime; } - function findHostInstance(fiber) { - var hostFiber = findCurrentHostFiber(fiber); - if (hostFiber === null) { - return null; + function deferredUpdates(fn) { + var previousExpirationContext = expirationContext; + expirationContext = computeAsyncExpiration(); + try { + return fn(); + } finally { + expirationContext = previousExpirationContext; } - return hostFiber.stateNode; } - return { - createContainer: function(containerInfo, hydrate) { - return createFiberRoot(containerInfo, hydrate); - }, - updateContainer: function(element, container, parentComponent, callback) { - var current = container.current; - var expirationTime = computeRootExpirationTime(current, element); - return updateContainerAtExpirationTime( - element, - container, - parentComponent, - expirationTime, - callback - ); - }, - - updateContainerAtExpirationTime: updateContainerAtExpirationTime, - - flushRoot: flushRoot, - - requestWork: requestWork, + function syncUpdates(fn) { + var previousExpirationContext = expirationContext; + expirationContext = Sync; + try { + return fn(); + } finally { + expirationContext = previousExpirationContext; + } + } - computeUniqueAsyncExpiration: computeUniqueAsyncExpiration, + // TODO: Everything below this is written as if it has been lifted to the + // renderers. I'll do this in a follow-up. - batchedUpdates: batchedUpdates, + // Linked-list of roots + var firstScheduledRoot = null; + var lastScheduledRoot = null; - unbatchedUpdates: unbatchedUpdates, + 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; - deferredUpdates: deferredUpdates, + var isBatchingUpdates = false; + var isUnbatchingUpdates = false; - flushSync: flushSync, + var completedBatches = null; - 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; - } - }, + // Use these to prevent an infinite loop of nested updates + var NESTED_UPDATE_LIMIT = 1000; + var nestedUpdateCount = 0; - findHostInstance: findHostInstance, + var timeHeuristicForUnitOfWork = 1; - findHostInstanceWithNoPortals: function(fiber) { - var hostFiber = findCurrentHostFiberWithNoPortals(fiber); - if (hostFiber === null) { - return null; + 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); } - return hostFiber.stateNode; - }, - injectIntoDevTools: function(devToolsConfig) { - var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance; - - return injectInternals( - Object.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); - } - }) - ); + // The request callback timer is already running. Don't start a new one. + } else { + startRequestCallbackTimer(); } - }; -}; -var ReactFiberReconciler$2 = Object.freeze({ - default: ReactFiberReconciler$1 -}); + // Compute a timeout for the given expiration time. + var currentMs = now() - startTime; + var expirationMs = expirationTimeToMs(expirationTime); + var timeout = expirationMs - currentMs; -var ReactFiberReconciler$3 = - (ReactFiberReconciler$2 && ReactFiberReconciler$1) || ReactFiberReconciler$2; + callbackExpirationTime = expirationTime; + callbackID = scheduleDeferredCallback(performAsyncWork, { + timeout: timeout + }); + } -// TODO: bundle Flow types with the package. + // 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." + ); + } -// 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; + // 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; + } + } -var viewConfigCallbacks = new Map(); -var viewConfigs = new Map(); + if (isRendering) { + // Prevent reentrancy. Remaining work will be scheduled at the end of + // the currently rendering batch. + return; + } -/** - * Registers a native view/component by name. - * A callback is provided to load the view config from UIManager. - * The callback is deferred until the view is actually rendered. - * This is done to avoid causing Prepack deopts. - */ -function register(name, callback) { - invariant( - !viewConfigCallbacks.has(name), - "Tried to register two views with the same name %s", - name - ); - viewConfigCallbacks.set(name, callback); - return name; -} + 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(root, Sync, recalculateCurrentTime()); + } + return; + } -/** - * Retrieves a config for the specified view. - * If this is the first time the view has been used, - * This configuration will be lazy-loaded from UIManager. - */ -function get$1(name) { - var viewConfig = void 0; - if (!viewConfigs.has(name)) { - var callback = viewConfigCallbacks.get(name); - invariant( - typeof callback === "function", - "View config not found for name %s", - name - ); - viewConfigCallbacks.set(name, null); - viewConfig = callback(); - viewConfigs.set(name, viewConfig); - } else { - viewConfig = viewConfigs.get(name); + // TODO: Get rid of Sync and use current time? + if (expirationTime === Sync) { + performWork(Sync, null); + } else { + scheduleCallbackWithExpiration(expirationTime); + } } - invariant(viewConfig, "View config not found for name %s", name); - return viewConfig; -} -function _classCallCheck$1(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } -} + function findHighestPriorityRoot() { + var highestPriorityWork = NoWork; + var highestPriorityRoot = null; -// Modules provided by RN: -/** - * This component defines the same methods as NativeMethodsMixin but without the - * findNodeHandle wrapper. This wrapper is unnecessary for HostComponent views - * and would also result in a circular require.js dependency (since - * ReactNativeFiber depends on this component and NativeMethodsMixin depends on - * ReactNativeFiber). - */ + 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. -var ReactNativeFiberHostComponent = (function() { - function ReactNativeFiberHostComponent(tag, viewConfig) { - _classCallCheck$1(this, ReactNativeFiberHostComponent); + // 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. + invariant( + previousScheduledRoot !== null && lastScheduledRoot !== null, + "Should have a previous and last root. This error is likely " + + "caused by a bug in React. Please file an issue." + ); + 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; + } + } + } - this._nativeTag = tag; - this._children = []; - this.viewConfig = viewConfig; + // 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; } - ReactNativeFiberHostComponent.prototype.blur = function blur() { - TextInputState.blurTextInput(this._nativeTag); - }; - - ReactNativeFiberHostComponent.prototype.focus = function focus() { - TextInputState.focusTextInput(this._nativeTag); - }; - - ReactNativeFiberHostComponent.prototype.measure = function measure(callback) { - UIManager.measure(this._nativeTag, mountSafeCallback(this, callback)); - }; + function performAsyncWork(dl) { + performWork(NoWork, dl); + } - ReactNativeFiberHostComponent.prototype.measureInWindow = function measureInWindow( - callback - ) { - UIManager.measureInWindow( - this._nativeTag, - mountSafeCallback(this, callback) - ); - }; + function performWork(minExpirationTime, dl) { + deadline = dl; - ReactNativeFiberHostComponent.prototype.measureLayout = function measureLayout( - relativeToNativeNode, - onSuccess, - onFail /* currently unused */ - ) { - UIManager.measureLayout( - this._nativeTag, - relativeToNativeNode, - mountSafeCallback(this, onFail), - mountSafeCallback(this, onSuccess) - ); - }; + // Keep working on roots until there's no more work, or until the we reach + // the deadline. + findHighestPriorityRoot(); - ReactNativeFiberHostComponent.prototype.setNativeProps = function setNativeProps( - nativeProps - ) { - { - warnForStyleProps(nativeProps, this.viewConfig.validAttributes); + if (enableUserTimingAPI && deadline !== null) { + var didExpire = nextFlushedExpirationTime < recalculateCurrentTime(); + stopRequestCallbackTimer(didExpire); } - var updatePayload = create(nativeProps, this.viewConfig.validAttributes); - - // Avoid the overhead of bridge calls if there's no update. - // This is an expensive no-op for Android, and causes an unnecessary - // view invalidation for certain components (eg RCTTextInput) on iOS. - if (updatePayload != null) { - UIManager.updateView( - this._nativeTag, - this.viewConfig.uiViewClassName, - updatePayload + while ( + nextFlushedRoot !== null && + nextFlushedExpirationTime !== NoWork && + (minExpirationTime === NoWork || + nextFlushedExpirationTime <= minExpirationTime) && + !deadlineDidExpire + ) { + performWorkOnRoot( + nextFlushedRoot, + nextFlushedExpirationTime, + recalculateCurrentTime() ); + // Find the next highest priority work. + findHighestPriorityRoot(); } - }; - - return ReactNativeFiberHostComponent; -})(); -var hasNativePerformanceNow = - typeof performance === "object" && typeof performance.now === "function"; + // We're done flushing work. Either we ran out of time in this callback, + // or there's no more work left with sufficient priority. -var now = hasNativePerformanceNow - ? function() { - return performance.now(); + // 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); } - : function() { - return Date.now(); - }; -var scheduledCallback = null; -var frameDeadline = 0; + // Clean-up. + deadline = null; + deadlineDidExpire = false; + nestedUpdateCount = 0; -var frameDeadlineObject = { - timeRemaining: function() { - return frameDeadline - now(); + finishRendering(); } -}; - -function setTimeoutCallback() { - // TODO (bvaughn) Hard-coded 5ms unblocks initial async testing. - // React API probably changing to boolean rather than time remaining. - // Longer-term plan is to rewrite this using shared memory, - // And just return the value of the bit as the boolean. - frameDeadline = now() + 5; - var callback = scheduledCallback; - scheduledCallback = null; - if (callback !== null) { - callback(frameDeadlineObject); + function flushRoot(root, expirationTime) { + invariant( + !isRendering, + "work.commit(): Cannot commit while already rendering. This likely " + + "means you attempted to commit from inside a lifecycle method." + ); + // Perform work on root as if the given expiration time is the current time. + // This has the effect of synchronously flushing all work up to and + // including the given time. + performWorkOnRoot(root, expirationTime, expirationTime); + finishRendering(); } -} - -// RN has a poor polyfill for requestIdleCallback so we aren't using it. -// This implementation is only intended for short-term use anyway. -// We also don't implement cancel functionality b'c Fiber doesn't currently need it. -function scheduleDeferredCallback(callback) { - // We assume only one callback is scheduled at a time b'c that's how Fiber works. - scheduledCallback = callback; - return setTimeout(setTimeoutCallback, 1); -} - -function cancelDeferredCallback(callbackID) { - scheduledCallback = null; - clearTimeout(callbackID); -} -// Modules provided by RN: -function recursivelyUncacheFiberNode(node) { - if (typeof node === "number") { - // Leaf node (eg text) - uncacheFiberNode(node); - } else { - uncacheFiberNode(node._nativeTag); + function finishRendering() { + if (completedBatches !== null) { + var batches = completedBatches; + completedBatches = null; + for (var i = 0; i < batches.length; i++) { + var batch = batches[i]; + try { + batch._onComplete(); + } catch (error) { + if (!hasUnhandledError) { + hasUnhandledError = true; + unhandledError = error; + } + } + } + } - node._children.forEach(recursivelyUncacheFiberNode); + if (hasUnhandledError) { + var _error4 = unhandledError; + unhandledError = null; + hasUnhandledError = false; + throw _error4; + } } -} -var NativeRenderer = reactReconciler({ - appendInitialChild: function(parentInstance, child) { - parentInstance._children.push(child); - }, - createInstance: function( - type, - props, - rootContainerInstance, - hostContext, - internalInstanceHandle - ) { - var tag = ReactNativeTagHandles.allocateTag(); - var viewConfig = get$1(type); + function performWorkOnRoot(root, expirationTime, currentTime) { + invariant( + !isRendering, + "performWorkOnRoot was called recursively. This error is likely caused " + + "by a bug in React. Please file an issue." + ); - { - for (var key in viewConfig.validAttributes) { - if (props.hasOwnProperty(key)) { - deepFreezeAndThrowOnMutationInDev(props[key]); + isRendering = true; + + // Check if this is async work or sync/expired work. + if (expirationTime <= currentTime) { + // Flush sync work. + var finishedWork = root.finishedWork; + if (finishedWork !== null) { + // This root is already complete. We can commit it. + completeRoot(root, finishedWork, expirationTime); + } else { + root.finishedWork = null; + finishedWork = renderRoot(root, expirationTime); + if (finishedWork !== null) { + // We've completed the root. Commit it. + completeRoot(root, finishedWork, expirationTime); + } + } + } else { + // Flush async work. + var _finishedWork = root.finishedWork; + if (_finishedWork !== null) { + // This root is already complete. We can commit it. + completeRoot(root, _finishedWork, expirationTime); + } 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. + completeRoot(root, _finishedWork, expirationTime); + } else { + // There's no time left. Mark this root as complete. We'll come + // back and commit it later. + root.finishedWork = _finishedWork; + } } } } - var updatePayload = create(props, viewConfig.validAttributes); - - UIManager.createView( - tag, // reactTag - viewConfig.uiViewClassName, // viewName - rootContainerInstance, // rootTag - updatePayload - ); - - var component = new ReactNativeFiberHostComponent(tag, viewConfig); - - precacheFiberNode(internalInstanceHandle, tag); - updateFiberProps(tag, props); - - // Not sure how to avoid this cast. Flow is okay if the component is defined - // in the same file but if it's external it can't see the types. - return component; - }, - createTextInstance: function( - text, - rootContainerInstance, - hostContext, - internalInstanceHandle - ) { - var tag = ReactNativeTagHandles.allocateTag(); + isRendering = false; + } - UIManager.createView( - tag, // reactTag - "RCTRawText", // viewName - rootContainerInstance, // rootTag - { text: text } - ); + function completeRoot(root, finishedWork, expirationTime) { + // Check if there's a batch that matches this expiration time. + var firstBatch = root.firstBatch; + if (firstBatch !== null && firstBatch._expirationTime <= expirationTime) { + if (completedBatches === null) { + completedBatches = [firstBatch]; + } else { + completedBatches.push(firstBatch); + } + if (firstBatch._defer) { + // This root is blocked from committing by a batch. Unschedule it until + // we receive another update. + root.finishedWork = finishedWork; + root.remainingExpirationTime = NoWork; + return; + } + } - precacheFiberNode(internalInstanceHandle, tag); + // Commit the root. + root.finishedWork = null; + root.remainingExpirationTime = commitRoot(finishedWork); + } - return tag; - }, - finalizeInitialChildren: function( - parentInstance, - type, - props, - rootContainerInstance - ) { - // Don't send a no-op message over the bridge. - if (parentInstance._children.length === 0) { + // 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; + } - // Map from child objects to native tags. - // Either way we need to pass a copy of the Array to prevent it from being frozen. - var nativeTags = parentInstance._children.map(function(child) { - return typeof child === "number" - ? child // Leaf node (eg text) - : child._nativeTag; - }); - - UIManager.setChildren( - parentInstance._nativeTag, // containerTag - nativeTags + // TODO: Not happy about this hook. Conceptually, renderRoot should return a + // tuple of (isReadyForCommit, didError, error) + function onUncaughtError(error) { + invariant( + nextFlushedRoot !== null, + "Should be working on a root. This error is likely caused by a bug in " + + "React. Please file an issue." ); + // 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; + } + } - return false; - }, - getRootHostContext: function() { - return emptyObject; - }, - getChildHostContext: function() { - return emptyObject; - }, - getPublicInstance: function(instance) { - return instance; - }, + // 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); + } + } + } - now: now, + // 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(); + } - prepareForCommit: function() { - // Noop - }, - prepareUpdate: function( - instance, - type, - oldProps, - newProps, - rootContainerInstance, - hostContext - ) { - return emptyObject; - }, - resetAfterCommit: function() { - // Noop - }, + // 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; + invariant( + !isRendering, + "flushSync was called from inside a lifecycle method. It cannot be " + + "called when React is already rendering." + ); + performWork(Sync, null); + } + } - scheduleDeferredCallback: scheduleDeferredCallback, - cancelDeferredCallback: cancelDeferredCallback, + return { + computeExpirationForFiber: computeExpirationForFiber, + scheduleWork: scheduleWork, + requestWork: requestWork, + flushRoot: flushRoot, + batchedUpdates: batchedUpdates, + unbatchedUpdates: unbatchedUpdates, + flushSync: flushSync, + deferredUpdates: deferredUpdates, + computeUniqueAsyncExpiration: computeUniqueAsyncExpiration + }; +}; - shouldDeprioritizeSubtree: function(type, props) { - return false; - }, - shouldSetTextContent: function(type, props) { - // TODO (bvaughn) Revisit this decision. - // Always returning false simplifies the createInstance() implementation, - // But creates an additional child Fiber for raw text children. - // No additional native views are created though. - // It's not clear to me which is better so I'm deferring for now. - // More context @ github.com/facebook/react/pull/8560#discussion_r92111303 - return false; - }, +var didWarnAboutNestedUpdates = void 0; - useSyncScheduling: true, +{ + didWarnAboutNestedUpdates = false; +} - mutation: { - appendChild: function(parentInstance, child) { - var childTag = typeof child === "number" ? child : child._nativeTag; - var children = parentInstance._children; - var index = children.indexOf(child); +// 0 is PROD, 1 is DEV. +// Might add PROFILE later. - if (index >= 0) { - children.splice(index, 1); - children.push(child); +function getContextForSubtree(parentComponent) { + if (!parentComponent) { + return emptyObject; + } - UIManager.manageChildren( - parentInstance._nativeTag, // containerTag - [index], // moveFromIndices - [children.length - 1], // moveToIndices - [], // addChildReactTags - [], // addAtIndices - [] - ); - } else { - children.push(child); + var fiber = get(parentComponent); + var parentContext = findCurrentUnmaskedContext(fiber); + return isContextProvider(fiber) + ? processChildContext(fiber, parentContext) + : parentContext; +} - UIManager.manageChildren( - parentInstance._nativeTag, // containerTag - [], // moveFromIndices - [], // moveToIndices - [childTag], // addChildReactTags - [children.length - 1], // addAtIndices - [] +var ReactFiberReconciler$1 = function(config) { + var getPublicInstance = config.getPublicInstance; + + var _ReactFiberScheduler = ReactFiberScheduler(config), + computeUniqueAsyncExpiration = + _ReactFiberScheduler.computeUniqueAsyncExpiration, + computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber, + scheduleWork = _ReactFiberScheduler.scheduleWork, + requestWork = _ReactFiberScheduler.requestWork, + flushRoot = _ReactFiberScheduler.flushRoot, + batchedUpdates = _ReactFiberScheduler.batchedUpdates, + unbatchedUpdates = _ReactFiberScheduler.unbatchedUpdates, + flushSync = _ReactFiberScheduler.flushSync, + deferredUpdates = _ReactFiberScheduler.deferredUpdates; + + function scheduleRootUpdate(current, element, expirationTime, 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" ); } - }, - appendChildToContainer: function(parentInstance, child) { - var childTag = typeof child === "number" ? child : child._nativeTag; - UIManager.setChildren( - parentInstance, // containerTag - [childTag] - ); - }, - commitTextUpdate: function(textInstance, oldText, newText) { - UIManager.updateView( - textInstance, // reactTag - "RCTRawText", // viewName - { text: newText } + } + + 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 ); - }, - commitMount: function(instance, type, newProps, internalInstanceHandle) { - // Noop - }, - commitUpdate: function( - instance, - updatePayloadTODO, - type, - oldProps, - newProps, - internalInstanceHandle - ) { - var viewConfig = instance.viewConfig; + } - updateFiberProps(instance._nativeTag, newProps); + var update = { + expirationTime: expirationTime, + partialState: { element: element }, + callback: callback, + isReplace: false, + isForced: false, + next: null + }; + insertUpdateIntoFiber(current, update); + scheduleWork(current, expirationTime); - var updatePayload = diff(oldProps, newProps, viewConfig.validAttributes); + return expirationTime; + } - // Avoid the overhead of bridge calls if there's no update. - // This is an expensive no-op for Android, and causes an unnecessary - // view invalidation for certain components (eg RCTTextInput) on iOS. - if (updatePayload != null) { - UIManager.updateView( - instance._nativeTag, // reactTag - viewConfig.uiViewClassName, // viewName - updatePayload - ); + function updateContainerAtExpirationTime( + element, + container, + parentComponent, + expirationTime, + 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); + } } - }, - insertBefore: function(parentInstance, child, beforeChild) { - var children = parentInstance._children; - var index = children.indexOf(child); + } - // Move existing child or add new child? - if (index >= 0) { - children.splice(index, 1); - var beforeChildIndex = children.indexOf(beforeChild); - children.splice(beforeChildIndex, 0, child); + var context = getContextForSubtree(parentComponent); + if (container.context === null) { + container.context = context; + } else { + container.pendingContext = context; + } - UIManager.manageChildren( - parentInstance._nativeTag, // containerID - [index], // moveFromIndices - [beforeChildIndex], // moveToIndices - [], // addChildReactTags - [], // addAtIndices - [] - ); - } else { - var _beforeChildIndex = children.indexOf(beforeChild); - children.splice(_beforeChildIndex, 0, child); + return scheduleRootUpdate(current, element, expirationTime, callback); + } - var childTag = typeof child === "number" ? child : child._nativeTag; + function findHostInstance(fiber) { + var hostFiber = findCurrentHostFiber(fiber); + if (hostFiber === null) { + return null; + } + return hostFiber.stateNode; + } - UIManager.manageChildren( - parentInstance._nativeTag, // containerID - [], // moveFromIndices - [], // moveToIndices - [childTag], // addChildReactTags - [_beforeChildIndex], // addAtIndices - [] - ); - } + return { + createContainer: function(containerInfo, isAsync, hydrate) { + return createFiberRoot(containerInfo, isAsync, hydrate); }, - insertInContainerBefore: function(parentInstance, child, beforeChild) { - // TODO (bvaughn): Remove this check when... - // We create a wrapper object for the container in ReactNative render() - // Or we refactor to remove wrapper objects entirely. - // For more info on pros/cons see PR #8560 description. - invariant( - typeof parentInstance !== "number", - "Container does not support insertBefore operation" + updateContainer: function(element, container, parentComponent, callback) { + var current = container.current; + var expirationTime = computeExpirationForFiber(current); + return updateContainerAtExpirationTime( + element, + container, + parentComponent, + expirationTime, + callback ); }, - removeChild: function(parentInstance, child) { - recursivelyUncacheFiberNode(child); - var children = parentInstance._children; - var index = children.indexOf(child); - children.splice(index, 1); + updateContainerAtExpirationTime: updateContainerAtExpirationTime, - UIManager.manageChildren( - parentInstance._nativeTag, // containerID - [], // moveFromIndices - [], // moveToIndices - [], // addChildReactTags - [], // addAtIndices - [index] - ); - }, - removeChildFromContainer: function(parentInstance, child) { - recursivelyUncacheFiberNode(child); - UIManager.manageChildren( - parentInstance, // containerID - [], // moveFromIndices - [], // moveToIndices - [], // addChildReactTags - [], // addAtIndices - [0] - ); - }, - resetTextContent: function(instance) { - // Noop - } - } -}); + flushRoot: flushRoot, -/** - * ReactNative vs ReactWeb - * ----------------------- - * React treats some pieces of data opaquely. This means that the information - * is first class (it can be passed around), but cannot be inspected. This - * allows us to build infrastructure that reasons about resources, without - * making assumptions about the nature of those resources, and this allows that - * infra to be shared across multiple platforms, where the resources are very - * different. General infra (such as `ReactMultiChild`) reasons opaquely about - * the data, but platform specific code (such as `ReactNativeBaseComponent`) can - * make assumptions about the data. - * - * - * `rootNodeID`, uniquely identifies a position in the generated native view - * tree. Many layers of composite components (created with `React.createClass`) - * can all share the same `rootNodeID`. - * - * `nodeHandle`: A sufficiently unambiguous way to refer to a lower level - * resource (dom node, native view etc). The `rootNodeID` is sufficient for web - * `nodeHandle`s, because the position in a tree is always enough to uniquely - * identify a DOM node (we never have nodes in some bank outside of the - * document). The same would be true for `ReactNative`, but we must maintain a - * mapping that we can send efficiently serializable - * strings across native boundaries. - * - * Opaque name TodaysWebReact FutureWebWorkerReact ReactNative - * ---------------------------------------------------------------------------- - * nodeHandle N/A rootNodeID tag - */ + requestWork: requestWork, -// TODO (bvaughn) Rename the findNodeHandle module to something more descriptive -// eg findInternalHostInstance. This will reduce the likelihood of someone -// accidentally deep-requiring this version. -function findNodeHandle(componentOrHandle) { - { - var owner = ReactCurrentOwner.current; - if (owner !== null && owner.stateNode !== null) { - warning( - owner.stateNode._warnedAboutRefsInRender, - "%s is accessing findNodeHandle inside its render(). " + - "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(owner) || "A component" - ); + computeUniqueAsyncExpiration: computeUniqueAsyncExpiration, + + 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; + } + }, - owner.stateNode._warnedAboutRefsInRender = true; - } - } - if (componentOrHandle == null) { - return null; - } - if (typeof componentOrHandle === "number") { - // Already a node handle - return componentOrHandle; - } + findHostInstance: findHostInstance, - var component = componentOrHandle; + findHostInstanceWithNoPortals: function(fiber) { + var hostFiber = findCurrentHostFiberWithNoPortals(fiber); + if (hostFiber === null) { + return null; + } + return hostFiber.stateNode; + }, + injectIntoDevTools: function(devToolsConfig) { + var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance; - // TODO (balpert): Wrap iOS native components in a composite wrapper, then - // ReactInstanceMap.get here will always succeed for mounted components - var internalInstance = get(component); - if (internalInstance) { - return NativeRenderer.findHostInstance(internalInstance); - } else { - if (component) { - return component; - } else { - invariant( - // Native - (typeof component === "object" && "_nativeTag" in component) || - // Composite - (component.render != null && typeof component.render === "function"), - "findNodeHandle(...): Argument is not a component " + - "(type: %s, keys: %s)", - typeof component, - Object.keys(component) - ); - invariant( - false, - "findNodeHandle(...): Unable to find node handle for unmounted " + - "component." + return injectInternals( + Object.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; + +var viewConfigCallbacks = new Map(); +var viewConfigs = new Map(); + +/** + * Registers a native view/component by name. + * A callback is provided to load the view config from UIManager. + * The callback is deferred until the view is actually rendered. + * This is done to avoid causing Prepack deopts. + */ +function register(name, callback) { + invariant( + !viewConfigCallbacks.has(name), + "Tried to register two views with the same name %s", + name + ); + viewConfigCallbacks.set(name, callback); + return name; } /** - * External users of findNodeHandle() expect the host tag number return type. - * The injected findNodeHandle() strategy returns the instance wrapper though. - * See NativeMethodsMixin#setNativeProps for more info on why this is done. + * Retrieves a config for the specified view. + * If this is the first time the view has been used, + * This configuration will be lazy-loaded from UIManager. */ -function findNumericNodeHandleFiber(componentOrHandle) { - var instance = findNodeHandle(componentOrHandle); - if (instance == null || typeof instance === "number") { - return instance; +function get$1(name) { + var viewConfig = void 0; + if (!viewConfigs.has(name)) { + var callback = viewConfigCallbacks.get(name); + invariant( + typeof callback === "function", + "View config not found for name %s", + name + ); + viewConfigCallbacks.set(name, null); + viewConfig = callback(); + viewConfigs.set(name, viewConfig); + } else { + viewConfig = viewConfigs.get(name); + } + invariant(viewConfig, "View config not found for name %s", name); + return viewConfig; +} + +function _classCallCheck$2(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); } - return instance._nativeTag; } // Modules provided by RN: /** - * `NativeMethodsMixin` provides methods to access the underlying native - * component directly. This can be useful in cases when you want to focus - * a view or measure its on-screen dimensions, for example. - * - * The methods described here are available on most of the default components - * provided by React Native. Note, however, that they are *not* available on - * composite components that aren't directly backed by a native view. This will - * generally include most components that you define in your own app. For more - * information, see [Direct - * Manipulation](docs/direct-manipulation.html). - * - * Note the Flow $Exact<> syntax is required to support mixins. - * React createClass mixins can only be used with exact types. + * This component defines the same methods as NativeMethodsMixin but without the + * findNodeHandle wrapper. This wrapper is unnecessary for HostComponent views + * and would also result in a circular require.js dependency (since + * ReactNativeFiber depends on this component and NativeMethodsMixin depends on + * ReactNativeFiber). */ -var NativeMethodsMixin = { - /** - * Determines the location on screen, width, and height of the given view and - * returns the values via an async callback. If successful, the callback will - * be called with the following arguments: - * - * - x - * - y - * - width - * - height - * - pageX - * - pageY - * - * Note that these measurements are not available until after the rendering - * has been completed in native. If you need the measurements as soon as - * possible, consider using the [`onLayout` - * prop](docs/view.html#onlayout) instead. - */ - measure: function(callback) { - UIManager.measure( - findNumericNodeHandleFiber(this), - mountSafeCallback(this, callback) - ); - }, - /** - * Determines the location of the given view in the window and returns the - * values via an async callback. If the React root view is embedded in - * another native view, this will give you the absolute coordinates. If - * successful, the callback will be called with the following - * arguments: - * - * - x - * - y - * - width - * - height - * - * Note that these measurements are not available until after the rendering - * has been completed in native. - */ - measureInWindow: function(callback) { +var ReactNativeFiberHostComponent = (function() { + function ReactNativeFiberHostComponent(tag, viewConfig) { + _classCallCheck$2(this, ReactNativeFiberHostComponent); + + this._nativeTag = tag; + this._children = []; + this.viewConfig = viewConfig; + } + + ReactNativeFiberHostComponent.prototype.blur = function blur() { + TextInputState.blurTextInput(this._nativeTag); + }; + + ReactNativeFiberHostComponent.prototype.focus = function focus() { + TextInputState.focusTextInput(this._nativeTag); + }; + + ReactNativeFiberHostComponent.prototype.measure = function measure(callback) { + UIManager.measure(this._nativeTag, mountSafeCallback(this, callback)); + }; + + ReactNativeFiberHostComponent.prototype.measureInWindow = function measureInWindow( + callback + ) { UIManager.measureInWindow( - findNumericNodeHandleFiber(this), + this._nativeTag, mountSafeCallback(this, callback) ); - }, + }; - /** - * Like [`measure()`](#measure), but measures the view relative an ancestor, - * specified as `relativeToNativeNode`. This means that the returned x, y - * are relative to the origin x, y of the ancestor view. - * - * As always, to obtain a native node handle for a component, you can use - * `findNumericNodeHandle(component)`. - */ - measureLayout: function( + ReactNativeFiberHostComponent.prototype.measureLayout = function measureLayout( relativeToNativeNode, onSuccess, onFail /* currently unused */ ) { UIManager.measureLayout( - findNumericNodeHandleFiber(this), + this._nativeTag, relativeToNativeNode, mountSafeCallback(this, onFail), mountSafeCallback(this, onSuccess) ); - }, - - /** - * This function sends props straight to native. They will not participate in - * future diff process - this means that if you do not include them in the - * next render, they will remain active (see [Direct - * Manipulation](docs/direct-manipulation.html)). - */ - setNativeProps: function(nativeProps) { - // Class components don't have viewConfig -> validateAttributes. - // Nor does it make sense to set native props on a non-native component. - // Instead, find the nearest host component and set props on it. - // Use findNodeHandle() rather than findNumericNodeHandle() because - // We want the instance/wrapper (not the native tag). - var maybeInstance = void 0; - - // Fiber errors if findNodeHandle is called for an umounted component. - // Tests using ReactTestRenderer will trigger this case indirectly. - // Mimicking stack behavior, we should silently ignore this case. - // TODO Fix ReactTestRenderer so we can remove this try/catch. - try { - maybeInstance = findNodeHandle(this); - } catch (error) {} - - // If there is no host component beneath this we should fail silently. - // This is not an error; it could mean a class component rendered null. - if (maybeInstance == null) { - return; - } - - var viewConfig = maybeInstance.viewConfig; + }; + ReactNativeFiberHostComponent.prototype.setNativeProps = function setNativeProps( + nativeProps + ) { { - warnForStyleProps(nativeProps, viewConfig.validAttributes); + warnForStyleProps(nativeProps, this.viewConfig.validAttributes); } - var updatePayload = create(nativeProps, viewConfig.validAttributes); + var updatePayload = create(nativeProps, this.viewConfig.validAttributes); // Avoid the overhead of bridge calls if there's no update. // This is an expensive no-op for Android, and causes an unnecessary // view invalidation for certain components (eg RCTTextInput) on iOS. if (updatePayload != null) { UIManager.updateView( - maybeInstance._nativeTag, - viewConfig.uiViewClassName, + this._nativeTag, + this.viewConfig.uiViewClassName, updatePayload ); } - }, + }; - /** - * Requests focus for the given input or view. The exact behavior triggered - * will depend on the platform and type of view. - */ - focus: function() { - TextInputState.focusTextInput(findNumericNodeHandleFiber(this)); - }, + return ReactNativeFiberHostComponent; +})(); - /** - * Removes focus from an input or view. This is the opposite of `focus()`. - */ - blur: function() { - TextInputState.blurTextInput(findNumericNodeHandleFiber(this)); +var hasNativePerformanceNow = + typeof performance === "object" && typeof performance.now === "function"; + +var now = hasNativePerformanceNow + ? function() { + return performance.now(); + } + : function() { + return Date.now(); + }; + +var scheduledCallback = null; +var frameDeadline = 0; + +var frameDeadlineObject = { + timeRemaining: function() { + return frameDeadline - now(); } }; -{ - // hide this from Flow since we can't define these properties outside of - // true without actually implementing them (setting them to undefined - // isn't allowed by ReactClass) - var NativeMethodsMixin_DEV = NativeMethodsMixin; - invariant( - !NativeMethodsMixin_DEV.componentWillMount && - !NativeMethodsMixin_DEV.componentWillReceiveProps, - "Do not override existing functions." - ); - NativeMethodsMixin_DEV.componentWillMount = function() { - throwOnStylesProp(this, this.props); - }; - NativeMethodsMixin_DEV.componentWillReceiveProps = function(newProps) { - throwOnStylesProp(this, newProps); - }; -} +function setTimeoutCallback() { + // TODO (bvaughn) Hard-coded 5ms unblocks initial async testing. + // React API probably changing to boolean rather than time remaining. + // Longer-term plan is to rewrite this using shared memory, + // And just return the value of the bit as the boolean. + frameDeadline = now() + 5; -function _classCallCheck$2(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); + var callback = scheduledCallback; + scheduledCallback = null; + if (callback !== null) { + callback(frameDeadlineObject); } } -function _possibleConstructorReturn(self, call) { - if (!self) { - throw new ReferenceError( - "this hasn't been initialised - super() hasn't been called" - ); - } - return call && (typeof call === "object" || typeof call === "function") - ? call - : self; +// RN has a poor polyfill for requestIdleCallback so we aren't using it. +// This implementation is only intended for short-term use anyway. +// We also don't implement cancel functionality b'c Fiber doesn't currently need it. +function scheduleDeferredCallback(callback) { + // We assume only one callback is scheduled at a time b'c that's how Fiber works. + scheduledCallback = callback; + return setTimeout(setTimeoutCallback, 1); } -function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError( - "Super expression must either be null or a function, not " + - typeof superClass - ); - } - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - enumerable: false, - writable: true, - configurable: true - } - }); - if (superClass) - Object.setPrototypeOf - ? Object.setPrototypeOf(subClass, superClass) - : (subClass.__proto__ = superClass); +function cancelDeferredCallback(callbackID) { + scheduledCallback = null; + clearTimeout(callbackID); } // Modules provided by RN: -/** - * Superclass that provides methods to access the underlying native component. - * This can be useful when you want to focus a view or measure its dimensions. - * - * Methods implemented by this class are available on most default components - * provided by React Native. However, they are *not* available on composite - * components that are not directly backed by a native view. For more - * information, see [Direct Manipulation](docs/direct-manipulation.html). - * - * @abstract - */ +function recursivelyUncacheFiberNode(node) { + if (typeof node === "number") { + // Leaf node (eg text) + uncacheFiberNode(node); + } else { + uncacheFiberNode(node._nativeTag); -var ReactNativeComponent = (function(_React$Component) { - _inherits(ReactNativeComponent, _React$Component); + node._children.forEach(recursivelyUncacheFiberNode); + } +} - function ReactNativeComponent() { - _classCallCheck$2(this, ReactNativeComponent); +var NativeRenderer = reactReconciler({ + appendInitialChild: function(parentInstance, child) { + parentInstance._children.push(child); + }, + createInstance: function( + type, + props, + rootContainerInstance, + hostContext, + internalInstanceHandle + ) { + var tag = ReactNativeTagHandles.allocateTag(); + var viewConfig = get$1(type); - return _possibleConstructorReturn( - this, - _React$Component.apply(this, arguments) + { + for (var key in viewConfig.validAttributes) { + if (props.hasOwnProperty(key)) { + deepFreezeAndThrowOnMutationInDev(props[key]); + } + } + } + + var updatePayload = create(props, viewConfig.validAttributes); + + UIManager.createView( + tag, // reactTag + viewConfig.uiViewClassName, // viewName + rootContainerInstance, // rootTag + updatePayload ); - } - /** - * Removes focus. This is the opposite of `focus()`. - */ - ReactNativeComponent.prototype.blur = function blur() { - TextInputState.blurTextInput(findNumericNodeHandleFiber(this)); - }; + var component = new ReactNativeFiberHostComponent(tag, viewConfig); - /** - * Requests focus. The exact behavior depends on the platform and view. - */ + precacheFiberNode(internalInstanceHandle, tag); + updateFiberProps(tag, props); + + // Not sure how to avoid this cast. Flow is okay if the component is defined + // in the same file but if it's external it can't see the types. + return component; + }, + createTextInstance: function( + text, + rootContainerInstance, + hostContext, + internalInstanceHandle + ) { + var tag = ReactNativeTagHandles.allocateTag(); + + UIManager.createView( + tag, // reactTag + "RCTRawText", // viewName + rootContainerInstance, // rootTag + { text: text } + ); + + precacheFiberNode(internalInstanceHandle, tag); + + return tag; + }, + finalizeInitialChildren: function( + parentInstance, + type, + props, + rootContainerInstance + ) { + // Don't send a no-op message over the bridge. + if (parentInstance._children.length === 0) { + return false; + } + + // Map from child objects to native tags. + // Either way we need to pass a copy of the Array to prevent it from being frozen. + var nativeTags = parentInstance._children.map(function(child) { + return typeof child === "number" + ? child // Leaf node (eg text) + : child._nativeTag; + }); + + UIManager.setChildren( + parentInstance._nativeTag, // containerTag + nativeTags + ); + + return false; + }, + getRootHostContext: function() { + return emptyObject; + }, + getChildHostContext: function() { + return emptyObject; + }, + getPublicInstance: function(instance) { + return instance; + }, + + now: now, + + prepareForCommit: function() { + // Noop + }, + prepareUpdate: function( + instance, + type, + oldProps, + newProps, + rootContainerInstance, + hostContext + ) { + return emptyObject; + }, + resetAfterCommit: function() { + // Noop + }, - ReactNativeComponent.prototype.focus = function focus() { - TextInputState.focusTextInput(findNumericNodeHandleFiber(this)); - }; + scheduleDeferredCallback: scheduleDeferredCallback, + cancelDeferredCallback: cancelDeferredCallback, - /** - * Measures the on-screen location and dimensions. If successful, the callback - * will be called asynchronously with the following arguments: - * - * - x - * - y - * - width - * - height - * - pageX - * - pageY - * - * These values are not available until after natives rendering completes. If - * you need the measurements as soon as possible, consider using the - * [`onLayout` prop](docs/view.html#onlayout) instead. - */ + shouldDeprioritizeSubtree: function(type, props) { + return false; + }, + shouldSetTextContent: function(type, props) { + // TODO (bvaughn) Revisit this decision. + // Always returning false simplifies the createInstance() implementation, + // But creates an additional child Fiber for raw text children. + // No additional native views are created though. + // It's not clear to me which is better so I'm deferring for now. + // More context @ github.com/facebook/react/pull/8560#discussion_r92111303 + return false; + }, - ReactNativeComponent.prototype.measure = function measure(callback) { - UIManager.measure( - findNumericNodeHandleFiber(this), - mountSafeCallback(this, callback) - ); - }; + mutation: { + appendChild: function(parentInstance, child) { + var childTag = typeof child === "number" ? child : child._nativeTag; + var children = parentInstance._children; + var index = children.indexOf(child); - /** - * Measures the on-screen location and dimensions. Even if the React Native - * root view is embedded within another native view, this method will give you - * the absolute coordinates measured from the window. If successful, the - * callback will be called asynchronously with the following arguments: - * - * - x - * - y - * - width - * - height - * - * These values are not available until after natives rendering completes. - */ + if (index >= 0) { + children.splice(index, 1); + children.push(child); - ReactNativeComponent.prototype.measureInWindow = function measureInWindow( - callback - ) { - UIManager.measureInWindow( - findNumericNodeHandleFiber(this), - mountSafeCallback(this, callback) - ); - }; + UIManager.manageChildren( + parentInstance._nativeTag, // containerTag + [index], // moveFromIndices + [children.length - 1], // moveToIndices + [], // addChildReactTags + [], // addAtIndices + [] + ); + } else { + children.push(child); - /** - * Similar to [`measure()`](#measure), but the resulting location will be - * relative to the supplied ancestor's location. - * - * Obtain a native node handle with `ReactNative.findNodeHandle(component)`. - */ + UIManager.manageChildren( + parentInstance._nativeTag, // containerTag + [], // moveFromIndices + [], // moveToIndices + [childTag], // addChildReactTags + [children.length - 1], // addAtIndices + [] + ); + } + }, + appendChildToContainer: function(parentInstance, child) { + var childTag = typeof child === "number" ? child : child._nativeTag; + UIManager.setChildren( + parentInstance, // containerTag + [childTag] + ); + }, + commitTextUpdate: function(textInstance, oldText, newText) { + UIManager.updateView( + textInstance, // reactTag + "RCTRawText", // viewName + { text: newText } + ); + }, + commitMount: function(instance, type, newProps, internalInstanceHandle) { + // Noop + }, + commitUpdate: function( + instance, + updatePayloadTODO, + type, + oldProps, + newProps, + internalInstanceHandle + ) { + var viewConfig = instance.viewConfig; - ReactNativeComponent.prototype.measureLayout = function measureLayout( - relativeToNativeNode, - onSuccess, - onFail /* currently unused */ - ) { - UIManager.measureLayout( - findNumericNodeHandleFiber(this), - relativeToNativeNode, - mountSafeCallback(this, onFail), - mountSafeCallback(this, onSuccess) - ); - }; + updateFiberProps(instance._nativeTag, newProps); - /** - * This function sends props straight to native. They will not participate in - * future diff process - this means that if you do not include them in the - * next render, they will remain active (see [Direct - * Manipulation](docs/direct-manipulation.html)). - */ + var updatePayload = diff(oldProps, newProps, viewConfig.validAttributes); - ReactNativeComponent.prototype.setNativeProps = function setNativeProps( - nativeProps - ) { - // Class components don't have viewConfig -> validateAttributes. - // Nor does it make sense to set native props on a non-native component. - // Instead, find the nearest host component and set props on it. - // Use findNodeHandle() rather than ReactNative.findNodeHandle() because - // We want the instance/wrapper (not the native tag). - var maybeInstance = void 0; + // Avoid the overhead of bridge calls if there's no update. + // This is an expensive no-op for Android, and causes an unnecessary + // view invalidation for certain components (eg RCTTextInput) on iOS. + if (updatePayload != null) { + UIManager.updateView( + instance._nativeTag, // reactTag + viewConfig.uiViewClassName, // viewName + updatePayload + ); + } + }, + insertBefore: function(parentInstance, child, beforeChild) { + var children = parentInstance._children; + var index = children.indexOf(child); - // Fiber errors if findNodeHandle is called for an umounted component. - // Tests using ReactTestRenderer will trigger this case indirectly. - // Mimicking stack behavior, we should silently ignore this case. - // TODO Fix ReactTestRenderer so we can remove this try/catch. - try { - maybeInstance = findNodeHandle(this); - } catch (error) {} + // Move existing child or add new child? + if (index >= 0) { + children.splice(index, 1); + var beforeChildIndex = children.indexOf(beforeChild); + children.splice(beforeChildIndex, 0, child); - // If there is no host component beneath this we should fail silently. - // This is not an error; it could mean a class component rendered null. - if (maybeInstance == null) { - return; - } + UIManager.manageChildren( + parentInstance._nativeTag, // containerID + [index], // moveFromIndices + [beforeChildIndex], // moveToIndices + [], // addChildReactTags + [], // addAtIndices + [] + ); + } else { + var _beforeChildIndex = children.indexOf(beforeChild); + children.splice(_beforeChildIndex, 0, child); - var viewConfig = maybeInstance.viewConfig; + var childTag = typeof child === "number" ? child : child._nativeTag; - var updatePayload = create(nativeProps, viewConfig.validAttributes); + UIManager.manageChildren( + parentInstance._nativeTag, // containerID + [], // moveFromIndices + [], // moveToIndices + [childTag], // addChildReactTags + [_beforeChildIndex], // addAtIndices + [] + ); + } + }, + insertInContainerBefore: function(parentInstance, child, beforeChild) { + // TODO (bvaughn): Remove this check when... + // We create a wrapper object for the container in ReactNative render() + // Or we refactor to remove wrapper objects entirely. + // For more info on pros/cons see PR #8560 description. + invariant( + typeof parentInstance !== "number", + "Container does not support insertBefore operation" + ); + }, + removeChild: function(parentInstance, child) { + recursivelyUncacheFiberNode(child); + var children = parentInstance._children; + var index = children.indexOf(child); - // Avoid the overhead of bridge calls if there's no update. - // This is an expensive no-op for Android, and causes an unnecessary - // view invalidation for certain components (eg RCTTextInput) on iOS. - if (updatePayload != null) { - UIManager.updateView( - maybeInstance._nativeTag, - viewConfig.uiViewClassName, - updatePayload + children.splice(index, 1); + + UIManager.manageChildren( + parentInstance._nativeTag, // containerID + [], // moveFromIndices + [], // moveToIndices + [], // addChildReactTags + [], // addAtIndices + [index] + ); + }, + removeChildFromContainer: function(parentInstance, child) { + recursivelyUncacheFiberNode(child); + UIManager.manageChildren( + parentInstance, // containerID + [], // moveFromIndices + [], // moveToIndices + [], // addChildReactTags + [], // addAtIndices + [0] ); + }, + resetTextContent: function(instance) { + // Noop } - }; - - return ReactNativeComponent; -})(React.Component); + } +}); // Module provided by RN: var getInspectorDataForViewTag = void 0; @@ -12892,16 +13657,13 @@ function takeSnapshot(view, options) { return UIManager.__takeSnapshot(view, options); } -// TODO: direct imports like some-package/src/* are bad. Fix me. // Module provided by RN: +injectFindHostInstance(NativeRenderer.findHostInstance); + injection$2.injectFiberBatchedUpdates(NativeRenderer.batchedUpdates); var roots = new Map(); -// Intercept lifecycle errors and ensure they are shown with the correct stack -// trace within the native redbox component. -injection$4.injectDialog(showDialog$1); - var ReactNativeRenderer = { NativeComponent: ReactNativeComponent, @@ -12913,7 +13675,7 @@ var ReactNativeRenderer = { if (!root) { // TODO (bvaughn): If we decide to keep the wrapper component, // We could create a wrapper for containerTag as well to reduce special casing. - root = NativeRenderer.createContainer(containerTag, false); + root = NativeRenderer.createContainer(containerTag, false, false); roots.set(containerTag, root); } NativeRenderer.updateContainer(element, root, null, callback); diff --git a/Libraries/Renderer/ReactNativeRenderer-prod.js b/Libraries/Renderer/ReactNativeRenderer-prod.js index a7497b75b30432..62540b0c53b073 100644 --- a/Libraries/Renderer/ReactNativeRenderer-prod.js +++ b/Libraries/Renderer/ReactNativeRenderer-prod.js @@ -16,78 +16,68 @@ var invariant = require("fbjs/lib/invariant"), RCTEventEmitter = require("RCTEventEmitter"), UIManager = require("UIManager"), React = require("react"), - ExceptionsManager = require("ExceptionsManager"), TextInputState = require("TextInputState"), deepDiffer = require("deepDiffer"), flattenStyle = require("flattenStyle"), emptyObject = require("fbjs/lib/emptyObject"), shallowEqual = require("fbjs/lib/shallowEqual"), - ReactErrorUtils = { - _caughtError: null, - _hasCaughtError: !1, - _rethrowError: null, - _hasRethrowError: !1, - injection: { - injectErrorUtils: function(injectedErrorUtils) { - invariant( - "function" === typeof injectedErrorUtils.invokeGuardedCallback, - "Injected invokeGuardedCallback() must be a function." - ); - invokeGuardedCallback = injectedErrorUtils.invokeGuardedCallback; - } - }, - invokeGuardedCallback: function(name, func, context, a, b, c, d, e, f) { - invokeGuardedCallback.apply(ReactErrorUtils, arguments); - }, - invokeGuardedCallbackAndCatchFirstError: function( - name, - func, - context, - a, - b, - c, - d, - e, - f - ) { - ReactErrorUtils.invokeGuardedCallback.apply(this, arguments); - if (ReactErrorUtils.hasCaughtError()) { - var error = ReactErrorUtils.clearCaughtError(); - ReactErrorUtils._hasRethrowError || - ((ReactErrorUtils._hasRethrowError = !0), - (ReactErrorUtils._rethrowError = error)); - } - }, - 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 = !1; - return error; - } - invariant( - !1, - "clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue." - ); - } - }; + ExceptionsManager = require("ExceptionsManager"); function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) { - ReactErrorUtils._hasCaughtError = !1; - ReactErrorUtils._caughtError = null; + this._hasCaughtError = !1; + this._caughtError = null; var funcArgs = Array.prototype.slice.call(arguments, 3); try { func.apply(context, funcArgs); } catch (error) { - (ReactErrorUtils._caughtError = error), - (ReactErrorUtils._hasCaughtError = !0); + (this._caughtError = error), (this._hasCaughtError = !0); } } +var ReactErrorUtils = { + _caughtError: null, + _hasCaughtError: !1, + _rethrowError: null, + _hasRethrowError: !1, + invokeGuardedCallback: function(name, func, context, a, b, c, d, e, f) { + invokeGuardedCallback.apply(ReactErrorUtils, arguments); + }, + invokeGuardedCallbackAndCatchFirstError: function( + name, + func, + context, + a, + b, + c, + d, + e, + f + ) { + ReactErrorUtils.invokeGuardedCallback.apply(this, arguments); + if (ReactErrorUtils.hasCaughtError()) { + var error = ReactErrorUtils.clearCaughtError(); + ReactErrorUtils._hasRethrowError || + ((ReactErrorUtils._hasRethrowError = !0), + (ReactErrorUtils._rethrowError = error)); + } + }, + 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 = !1; + return error; + } + invariant( + !1, + "clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue." + ); + } +}; function rethrowCaughtError() { if (ReactErrorUtils._hasRethrowError) { var error = ReactErrorUtils._rethrowError; @@ -446,16 +436,21 @@ Object.assign(SyntheticEvent.prototype, { } }); SyntheticEvent.Interface = EventInterface; -SyntheticEvent.augmentClass = function(Class, Interface) { +SyntheticEvent.extend = function(Interface) { function E() {} - E.prototype = this.prototype; + function Class() { + return Super.apply(this, arguments); + } + var Super = this; + E.prototype = Super.prototype; var prototype = new E(); Object.assign(prototype, Class.prototype); Class.prototype = prototype; Class.prototype.constructor = Class; - Class.Interface = Object.assign({}, this.Interface, Interface); - Class.augmentClass = this.augmentClass; + Class.Interface = Object.assign({}, Super.Interface, Interface); + Class.extend = Super.extend; addEventPoolingTo(Class); + return Class; }; addEventPoolingTo(SyntheticEvent); function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) { @@ -479,26 +474,12 @@ function addEventPoolingTo(EventConstructor) { EventConstructor.getPooled = getPooledEvent; EventConstructor.release = releasePooledEvent; } -function ResponderSyntheticEvent( - dispatchConfig, - dispatchMarker, - nativeEvent, - nativeEventTarget -) { - return SyntheticEvent.call( - this, - dispatchConfig, - dispatchMarker, - nativeEvent, - nativeEventTarget - ); -} -SyntheticEvent.augmentClass(ResponderSyntheticEvent, { - touchHistory: function() { - return null; - } -}); -var touchBank = [], +var ResponderSyntheticEvent = SyntheticEvent.extend({ + touchHistory: function() { + return null; + } + }), + touchBank = [], touchHistory = { touchBank: touchBank, numberActiveTouches: 0, @@ -708,7 +689,7 @@ var eventTypes = { isStartish(topLevelType) || isMoveish(topLevelType)) ) { - var shouldSetEventType = isStartish(topLevelType) + var JSCompiler_temp = isStartish(topLevelType) ? eventTypes.startShouldSetResponder : isMoveish(topLevelType) ? eventTypes.moveShouldSetResponder @@ -717,9 +698,9 @@ var eventTypes = { : eventTypes.scrollShouldSetResponder; if (responderInst) b: { - var JSCompiler_temp = responderInst; + var JSCompiler_temp$jscomp$0 = responderInst; for ( - var depthA = 0, tempA = JSCompiler_temp; + var depthA = 0, tempA = JSCompiler_temp$jscomp$0; tempA; tempA = getParent(tempA) ) @@ -728,196 +709,188 @@ var eventTypes = { for (var tempB = targetInst; tempB; tempB = getParent(tempB)) tempA++; for (; 0 < depthA - tempA; ) - (JSCompiler_temp = getParent(JSCompiler_temp)), depthA--; + (JSCompiler_temp$jscomp$0 = getParent(JSCompiler_temp$jscomp$0)), + depthA--; for (; 0 < tempA - depthA; ) (targetInst = getParent(targetInst)), tempA--; for (; depthA--; ) { if ( - JSCompiler_temp === targetInst || - JSCompiler_temp === targetInst.alternate + JSCompiler_temp$jscomp$0 === targetInst || + JSCompiler_temp$jscomp$0 === targetInst.alternate ) break b; - JSCompiler_temp = getParent(JSCompiler_temp); + JSCompiler_temp$jscomp$0 = getParent(JSCompiler_temp$jscomp$0); targetInst = getParent(targetInst); } - JSCompiler_temp = null; + JSCompiler_temp$jscomp$0 = null; } - else JSCompiler_temp = targetInst; - targetInst = JSCompiler_temp === responderInst; - JSCompiler_temp = ResponderSyntheticEvent.getPooled( - shouldSetEventType, + else JSCompiler_temp$jscomp$0 = targetInst; + targetInst = JSCompiler_temp$jscomp$0 === responderInst; + JSCompiler_temp$jscomp$0 = ResponderSyntheticEvent.getPooled( JSCompiler_temp, + JSCompiler_temp$jscomp$0, nativeEvent, nativeEventTarget ); - JSCompiler_temp.touchHistory = ResponderTouchHistoryStore.touchHistory; + JSCompiler_temp$jscomp$0.touchHistory = + ResponderTouchHistoryStore.touchHistory; targetInst ? forEachAccumulated( - JSCompiler_temp, + JSCompiler_temp$jscomp$0, accumulateTwoPhaseDispatchesSingleSkipTarget ) : forEachAccumulated( - JSCompiler_temp, + JSCompiler_temp$jscomp$0, accumulateTwoPhaseDispatchesSingle ); b: { - shouldSetEventType = JSCompiler_temp._dispatchListeners; - targetInst = JSCompiler_temp._dispatchInstances; - if (Array.isArray(shouldSetEventType)) + JSCompiler_temp = JSCompiler_temp$jscomp$0._dispatchListeners; + targetInst = JSCompiler_temp$jscomp$0._dispatchInstances; + if (Array.isArray(JSCompiler_temp)) for ( depthA = 0; - depthA < shouldSetEventType.length && - !JSCompiler_temp.isPropagationStopped(); + depthA < JSCompiler_temp.length && + !JSCompiler_temp$jscomp$0.isPropagationStopped(); depthA++ ) { if ( - shouldSetEventType[depthA](JSCompiler_temp, targetInst[depthA]) + JSCompiler_temp[depthA]( + JSCompiler_temp$jscomp$0, + targetInst[depthA] + ) ) { - shouldSetEventType = targetInst[depthA]; + JSCompiler_temp = targetInst[depthA]; break b; } } else if ( - shouldSetEventType && - shouldSetEventType(JSCompiler_temp, targetInst) + JSCompiler_temp && + JSCompiler_temp(JSCompiler_temp$jscomp$0, targetInst) ) { - shouldSetEventType = targetInst; + JSCompiler_temp = targetInst; break b; } - shouldSetEventType = null; + JSCompiler_temp = null; } - JSCompiler_temp._dispatchInstances = null; - JSCompiler_temp._dispatchListeners = null; - JSCompiler_temp.isPersistent() || - JSCompiler_temp.constructor.release(JSCompiler_temp); - if (shouldSetEventType && shouldSetEventType !== responderInst) - if ( - ((JSCompiler_temp = ResponderSyntheticEvent.getPooled( + JSCompiler_temp$jscomp$0._dispatchInstances = null; + JSCompiler_temp$jscomp$0._dispatchListeners = null; + JSCompiler_temp$jscomp$0.isPersistent() || + JSCompiler_temp$jscomp$0.constructor.release( + JSCompiler_temp$jscomp$0 + ); + JSCompiler_temp && JSCompiler_temp !== responderInst + ? ((JSCompiler_temp$jscomp$0 = void 0), + (targetInst = ResponderSyntheticEvent.getPooled( eventTypes.responderGrant, - shouldSetEventType, + JSCompiler_temp, nativeEvent, nativeEventTarget )), - (JSCompiler_temp.touchHistory = - ResponderTouchHistoryStore.touchHistory), - forEachAccumulated( - JSCompiler_temp, - accumulateDirectDispatchesSingle - ), - (targetInst = !0 === executeDirectDispatch(JSCompiler_temp)), - responderInst) - ) - if ( - ((depthA = ResponderSyntheticEvent.getPooled( - eventTypes.responderTerminationRequest, - responderInst, - nativeEvent, - nativeEventTarget - )), - (depthA.touchHistory = ResponderTouchHistoryStore.touchHistory), - forEachAccumulated(depthA, accumulateDirectDispatchesSingle), - (tempA = - !depthA._dispatchListeners || executeDirectDispatch(depthA)), - depthA.isPersistent() || depthA.constructor.release(depthA), - tempA) - ) { - depthA = ResponderSyntheticEvent.getPooled( - eventTypes.responderTerminate, - responderInst, - nativeEvent, - nativeEventTarget - ); - depthA.touchHistory = ResponderTouchHistoryStore.touchHistory; - forEachAccumulated(depthA, accumulateDirectDispatchesSingle); - var JSCompiler_temp$jscomp$0 = accumulate( - JSCompiler_temp$jscomp$0, - [JSCompiler_temp, depthA] - ); - changeResponder(shouldSetEventType, targetInst); - } else - (shouldSetEventType = ResponderSyntheticEvent.getPooled( - eventTypes.responderReject, - shouldSetEventType, - nativeEvent, - nativeEventTarget - )), - (shouldSetEventType.touchHistory = - ResponderTouchHistoryStore.touchHistory), - forEachAccumulated( - shouldSetEventType, - accumulateDirectDispatchesSingle - ), - (JSCompiler_temp$jscomp$0 = accumulate( + (targetInst.touchHistory = ResponderTouchHistoryStore.touchHistory), + forEachAccumulated(targetInst, accumulateDirectDispatchesSingle), + (depthA = !0 === executeDirectDispatch(targetInst)), + responderInst + ? ((tempA = ResponderSyntheticEvent.getPooled( + eventTypes.responderTerminationRequest, + responderInst, + nativeEvent, + nativeEventTarget + )), + (tempA.touchHistory = ResponderTouchHistoryStore.touchHistory), + forEachAccumulated(tempA, accumulateDirectDispatchesSingle), + (tempB = + !tempA._dispatchListeners || executeDirectDispatch(tempA)), + tempA.isPersistent() || tempA.constructor.release(tempA), + tempB + ? ((tempA = ResponderSyntheticEvent.getPooled( + eventTypes.responderTerminate, + responderInst, + nativeEvent, + nativeEventTarget + )), + (tempA.touchHistory = + ResponderTouchHistoryStore.touchHistory), + forEachAccumulated(tempA, accumulateDirectDispatchesSingle), + (JSCompiler_temp$jscomp$0 = accumulate( + JSCompiler_temp$jscomp$0, + [targetInst, tempA] + )), + changeResponder(JSCompiler_temp, depthA)) + : ((JSCompiler_temp = ResponderSyntheticEvent.getPooled( + eventTypes.responderReject, + JSCompiler_temp, + nativeEvent, + nativeEventTarget + )), + (JSCompiler_temp.touchHistory = + ResponderTouchHistoryStore.touchHistory), + forEachAccumulated( + JSCompiler_temp, + accumulateDirectDispatchesSingle + ), + (JSCompiler_temp$jscomp$0 = accumulate( + JSCompiler_temp$jscomp$0, + JSCompiler_temp + )))) + : ((JSCompiler_temp$jscomp$0 = accumulate( JSCompiler_temp$jscomp$0, - shouldSetEventType - )); - else - (JSCompiler_temp$jscomp$0 = accumulate( - JSCompiler_temp$jscomp$0, - JSCompiler_temp - )), - changeResponder(shouldSetEventType, targetInst); - else JSCompiler_temp$jscomp$0 = null; - } else JSCompiler_temp$jscomp$0 = null; - shouldSetEventType = responderInst && isStartish(topLevelType); - JSCompiler_temp = responderInst && isMoveish(topLevelType); - targetInst = responderInst && isEndish(topLevelType); + targetInst + )), + changeResponder(JSCompiler_temp, depthA)), + (JSCompiler_temp = JSCompiler_temp$jscomp$0)) + : (JSCompiler_temp = null); + } else JSCompiler_temp = null; + JSCompiler_temp$jscomp$0 = responderInst && isStartish(topLevelType); + targetInst = responderInst && isMoveish(topLevelType); + depthA = responderInst && isEndish(topLevelType); if ( - (shouldSetEventType = shouldSetEventType + (JSCompiler_temp$jscomp$0 = JSCompiler_temp$jscomp$0 ? eventTypes.responderStart - : JSCompiler_temp + : targetInst ? eventTypes.responderMove - : targetInst ? eventTypes.responderEnd : null) + : depthA ? eventTypes.responderEnd : null) ) - (shouldSetEventType = ResponderSyntheticEvent.getPooled( - shouldSetEventType, + (JSCompiler_temp$jscomp$0 = ResponderSyntheticEvent.getPooled( + JSCompiler_temp$jscomp$0, responderInst, nativeEvent, nativeEventTarget )), - (shouldSetEventType.touchHistory = + (JSCompiler_temp$jscomp$0.touchHistory = ResponderTouchHistoryStore.touchHistory), forEachAccumulated( - shouldSetEventType, + JSCompiler_temp$jscomp$0, accumulateDirectDispatchesSingle ), - (JSCompiler_temp$jscomp$0 = accumulate( - JSCompiler_temp$jscomp$0, - shouldSetEventType + (JSCompiler_temp = accumulate( + JSCompiler_temp, + JSCompiler_temp$jscomp$0 )); - shouldSetEventType = responderInst && "topTouchCancel" === topLevelType; + JSCompiler_temp$jscomp$0 = + responderInst && "topTouchCancel" === topLevelType; if ( (topLevelType = - responderInst && !shouldSetEventType && isEndish(topLevelType)) + responderInst && !JSCompiler_temp$jscomp$0 && isEndish(topLevelType)) ) a: { if ((topLevelType = nativeEvent.touches) && 0 !== topLevelType.length) - for ( - JSCompiler_temp = 0; - JSCompiler_temp < topLevelType.length; - JSCompiler_temp++ - ) + for (targetInst = 0; targetInst < topLevelType.length; targetInst++) if ( - ((targetInst = topLevelType[JSCompiler_temp].target), - null !== targetInst && - void 0 !== targetInst && - 0 !== targetInst) + ((depthA = topLevelType[targetInst].target), + null !== depthA && void 0 !== depthA && 0 !== depthA) ) { - depthA = getInstanceFromNode(targetInst); + tempA = getInstanceFromNode(depthA); b: { - for (targetInst = responderInst; depthA; ) { - if ( - targetInst === depthA || - targetInst === depthA.alternate - ) { - targetInst = !0; + for (depthA = responderInst; tempA; ) { + if (depthA === tempA || depthA === tempA.alternate) { + depthA = !0; break b; } - depthA = getParent(depthA); + tempA = getParent(tempA); } - targetInst = !1; + depthA = !1; } - if (targetInst) { + if (depthA) { topLevelType = !1; break a; } @@ -925,7 +898,7 @@ var eventTypes = { topLevelType = !0; } if ( - (topLevelType = shouldSetEventType + (topLevelType = JSCompiler_temp$jscomp$0 ? eventTypes.responderTerminate : topLevelType ? eventTypes.responderRelease : null) ) @@ -937,10 +910,7 @@ var eventTypes = { )), (nativeEvent.touchHistory = ResponderTouchHistoryStore.touchHistory), forEachAccumulated(nativeEvent, accumulateDirectDispatchesSingle), - (JSCompiler_temp$jscomp$0 = accumulate( - JSCompiler_temp$jscomp$0, - nativeEvent - )), + (JSCompiler_temp = accumulate(JSCompiler_temp, nativeEvent)), changeResponder(null); nativeEvent = ResponderTouchHistoryStore.touchHistory.numberActiveTouches; if ( @@ -949,7 +919,7 @@ var eventTypes = { ) ResponderEventPlugin.GlobalInteractionHandler.onChange(nativeEvent); previousActiveTouches = nativeEvent; - return JSCompiler_temp$jscomp$0; + return JSCompiler_temp; }, GlobalResponderHandler: null, GlobalInteractionHandler: null, @@ -1018,7 +988,7 @@ function uncacheFiberNode(tag) { delete instanceProps[tag]; } function getInstanceFromTag(tag) { - return instanceCache[tag] || null; + return "number" === typeof tag ? instanceCache[tag] || null : tag; } var ReactNativeComponentTree = Object.freeze({ precacheFiberNode: function(hostInst, tag) { @@ -1028,9 +998,10 @@ var ReactNativeComponentTree = Object.freeze({ getClosestInstanceFromNode: getInstanceFromTag, getInstanceFromNode: getInstanceFromTag, getNodeFromInstance: function(inst) { - inst = inst.stateNode._nativeTag; - invariant(inst, "All native instances should have a tag."); - return inst; + var tag = inst.stateNode._nativeTag; + void 0 === tag && (tag = inst.stateNode.canonical._nativeTag); + invariant(tag, "All native instances should have a tag."); + return tag; }, getFiberCurrentPropsFromNode: function(stateNode) { return instanceProps[stateNode._nativeTag] || null; @@ -1074,34 +1045,6 @@ function batchedUpdates(fn, bookkeeping) { restoreStateOfTarget(fn[bookkeeping]); } } -function handleTopLevel( - topLevelType, - targetInst, - nativeEvent, - nativeEventTarget -) { - for (var events, i = 0; i < plugins.length; i++) { - var possiblePlugin = plugins[i]; - possiblePlugin && - (possiblePlugin = possiblePlugin.extractEvents( - topLevelType, - targetInst, - nativeEvent, - nativeEventTarget - )) && - (events = accumulateInto(events, possiblePlugin)); - } - events && (eventQueue = accumulateInto(eventQueue, events)); - topLevelType = eventQueue; - eventQueue = null; - topLevelType && - (forEachAccumulated(topLevelType, executeDispatchesAndReleaseTopLevel), - invariant( - !eventQueue, - "processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented." - ), - ReactErrorUtils.rethrowCaughtError()); -} var ReactNativeTagHandles = { tagsStartAt: 1, tagCount: 1, @@ -1128,7 +1071,29 @@ function _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam) { var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT, inst = getInstanceFromTag(rootNodeID); batchedUpdates(function() { - handleTopLevel(topLevelType, inst, nativeEvent, nativeEvent.target); + var events = nativeEvent.target; + for (var events$jscomp$0 = null, i = 0; i < plugins.length; i++) { + var possiblePlugin = plugins[i]; + possiblePlugin && + (possiblePlugin = possiblePlugin.extractEvents( + topLevelType, + inst, + nativeEvent, + events + )) && + (events$jscomp$0 = accumulateInto(events$jscomp$0, possiblePlugin)); + } + events = events$jscomp$0; + null !== events && (eventQueue = accumulateInto(eventQueue, events)); + events = eventQueue; + eventQueue = null; + events && + (forEachAccumulated(events, executeDispatchesAndReleaseTopLevel), + invariant( + !eventQueue, + "processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented." + ), + ReactErrorUtils.rethrowCaughtError()); }); } var ReactNativeEventEmitter = Object.freeze({ @@ -1172,8 +1137,7 @@ var ReactNativeEventEmitter = Object.freeze({ (index = target); _receiveRootNodeIDEvent(index, eventTopLevelType, i); } - }, - handleTopLevel: handleTopLevel + } }); RCTEventEmitter.register(ReactNativeEventEmitter); injection.injectEventPluginOrder([ @@ -1195,16 +1159,17 @@ injection.injectEventPluginsByName({ ResponderEventPlugin: ResponderEventPlugin, ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin }); -function defaultShowDialog() { - return !0; -} -var showDialog = defaultShowDialog, - hasSymbol = "function" === typeof Symbol && Symbol["for"], +var hasSymbol = "function" === typeof Symbol && Symbol["for"], REACT_ELEMENT_TYPE = hasSymbol ? Symbol["for"]("react.element") : 60103, REACT_CALL_TYPE = hasSymbol ? Symbol["for"]("react.call") : 60104, REACT_RETURN_TYPE = hasSymbol ? Symbol["for"]("react.return") : 60105, REACT_PORTAL_TYPE = hasSymbol ? Symbol["for"]("react.portal") : 60106, REACT_FRAGMENT_TYPE = hasSymbol ? Symbol["for"]("react.fragment") : 60107, + REACT_STRICT_MODE_TYPE = hasSymbol + ? Symbol["for"]("react.strict_mode") + : 60108, + REACT_PROVIDER_TYPE = hasSymbol ? Symbol["for"]("react.provider") : 60109, + REACT_CONTEXT_TYPE = hasSymbol ? Symbol["for"]("react.context") : 60110, MAYBE_ITERATOR_SYMBOL = "function" === typeof Symbol && Symbol.iterator; function getIteratorFn(maybeIterable) { if (null === maybeIterable || "undefined" === typeof maybeIterable) @@ -1378,23 +1343,23 @@ function restoreDeletedValuesInNestedArray( else if (node && 0 < removedKeyCount) for (i in ((node = resolveObject(node)), removedKeys)) if (removedKeys[i]) { - var nextProp = node[i]; - if (void 0 !== nextProp) { + var _nextProp = node[i]; + if (void 0 !== _nextProp) { var attributeConfig = validAttributes[i]; if (attributeConfig) { - "function" === typeof nextProp && (nextProp = !0); - "undefined" === typeof nextProp && (nextProp = null); + "function" === typeof _nextProp && (_nextProp = !0); + "undefined" === typeof _nextProp && (_nextProp = null); if ("object" !== typeof attributeConfig) - updatePayload[i] = nextProp; + updatePayload[i] = _nextProp; else if ( "function" === typeof attributeConfig.diff || "function" === typeof attributeConfig.process ) - (nextProp = + (_nextProp = "function" === typeof attributeConfig.process - ? attributeConfig.process(nextProp) - : nextProp), - (updatePayload[i] = nextProp); + ? attributeConfig.process(_nextProp) + : _nextProp), + (updatePayload[i] = _nextProp); removedKeys[i] = !1; removedKeyCount--; } @@ -1559,19 +1524,19 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) { ), (removedKeys = null)); } - for (propKey in prevProps) - void 0 === nextProps[propKey] && - (!(attributeConfig = validAttributes[propKey]) || - (updatePayload && void 0 !== updatePayload[propKey]) || - ((prevProp = prevProps[propKey]), + for (var _propKey in prevProps) + void 0 === nextProps[_propKey] && + (!(attributeConfig = validAttributes[_propKey]) || + (updatePayload && void 0 !== updatePayload[_propKey]) || + ((prevProp = prevProps[_propKey]), void 0 !== prevProp && ("object" !== typeof attributeConfig || "function" === typeof attributeConfig.diff || "function" === typeof attributeConfig.process - ? (((updatePayload || (updatePayload = {}))[propKey] = null), + ? (((updatePayload || (updatePayload = {}))[_propKey] = null), removedKeys || (removedKeys = {}), - removedKeys[propKey] || - ((removedKeys[propKey] = !0), removedKeyCount++)) + removedKeys[_propKey] || + ((removedKeys[_propKey] = !0), removedKeyCount++)) : (updatePayload = clearNestedProperty( updatePayload, prevProp, @@ -1595,12 +1560,136 @@ function mountSafeCallback(context, callback) { } function getComponentName(fiber) { fiber = fiber.type; - return "string" === typeof fiber - ? fiber - : "function" === typeof fiber ? fiber.displayName || fiber.name : null; + if ("function" === typeof fiber) return fiber.displayName || fiber.name; + if ("string" === typeof fiber) return fiber; + switch (fiber) { + case REACT_FRAGMENT_TYPE: + return "ReactFragment"; + case REACT_PORTAL_TYPE: + return "ReactPortal"; + case REACT_CALL_TYPE: + return "ReactCall"; + case REACT_RETURN_TYPE: + return "ReactReturn"; + } + return null; +} +function findHostInstance() { + return null; +} +function findNodeHandle(componentOrHandle) { + if (null == componentOrHandle) return null; + if ("number" === typeof componentOrHandle) return componentOrHandle; + var internalInstance = componentOrHandle._reactInternalFiber; + if (internalInstance) return findHostInstance(internalInstance) || null; + if (componentOrHandle) return componentOrHandle; + invariant( + ("object" === typeof componentOrHandle && + "_nativeTag" in componentOrHandle) || + (null != componentOrHandle.render && + "function" === typeof componentOrHandle.render), + "findNodeHandle(...): Argument is not a component (type: %s, keys: %s)", + typeof componentOrHandle, + Object.keys(componentOrHandle) + ); + invariant( + !1, + "findNodeHandle(...): Unable to find node handle for unmounted component." + ); +} +function findNumericNodeHandleFiber(componentOrHandle) { + componentOrHandle = findNodeHandle(componentOrHandle); + return null == componentOrHandle || "number" === typeof componentOrHandle + ? componentOrHandle + : componentOrHandle._nativeTag; } -var debugRenderPhaseSideEffects = require("ReactFeatureFlags") - .debugRenderPhaseSideEffects; +function _inherits(subClass, superClass) { + if ("function" !== typeof superClass && null !== superClass) + throw new TypeError( + "Super expression must either be null or a function, not " + + typeof superClass + ); + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + enumerable: !1, + writable: !0, + configurable: !0 + } + }); + superClass && + (Object.setPrototypeOf + ? Object.setPrototypeOf(subClass, superClass) + : (subClass.__proto__ = superClass)); +} +var ReactNativeComponent = (function(_React$Component) { + function ReactNativeComponent() { + if (!(this instanceof ReactNativeComponent)) + throw new TypeError("Cannot call a class as a function"); + var call = _React$Component.apply(this, arguments); + if (!this) + throw new ReferenceError( + "this hasn't been initialised - super() hasn't been called" + ); + return !call || ("object" !== typeof call && "function" !== typeof call) + ? this + : call; + } + _inherits(ReactNativeComponent, _React$Component); + ReactNativeComponent.prototype.blur = function() { + TextInputState.blurTextInput(findNumericNodeHandleFiber(this)); + }; + ReactNativeComponent.prototype.focus = function() { + TextInputState.focusTextInput(findNumericNodeHandleFiber(this)); + }; + ReactNativeComponent.prototype.measure = function(callback) { + UIManager.measure( + findNumericNodeHandleFiber(this), + mountSafeCallback(this, callback) + ); + }; + ReactNativeComponent.prototype.measureInWindow = function(callback) { + UIManager.measureInWindow( + findNumericNodeHandleFiber(this), + mountSafeCallback(this, callback) + ); + }; + ReactNativeComponent.prototype.measureLayout = function( + relativeToNativeNode, + onSuccess, + onFail + ) { + UIManager.measureLayout( + findNumericNodeHandleFiber(this), + relativeToNativeNode, + mountSafeCallback(this, onFail), + mountSafeCallback(this, onSuccess) + ); + }; + ReactNativeComponent.prototype.setNativeProps = function(nativeProps) { + var maybeInstance = void 0; + try { + maybeInstance = findNodeHandle(this); + } catch (error) {} + if (null != maybeInstance) { + var viewConfig = + maybeInstance.viewConfig || maybeInstance.canonical.viewConfig; + nativeProps = diffProperties( + null, + emptyObject$1, + nativeProps, + viewConfig.validAttributes + ); + null != nativeProps && + UIManager.updateView( + maybeInstance._nativeTag, + viewConfig.uiViewClassName, + nativeProps + ); + } + }; + return ReactNativeComponent; +})(React.Component); function isFiberMountedImpl(fiber) { var node = fiber; if (fiber.alternate) for (; node["return"]; ) node = node["return"]; @@ -1741,6 +1830,10 @@ function push(cursor, value) { valueStack[index] = cursor.current; cursor.current = value; } +var _require = require("ReactFeatureFlags"), + debugRenderPhaseSideEffects = _require.debugRenderPhaseSideEffects, + debugRenderPhaseSideEffectsForStrictMode = + _require.debugRenderPhaseSideEffectsForStrictMode; new Set(); var contextStackCursor = { current: emptyObject }, didPerformWorkStackCursor = { current: !1 }, @@ -1842,13 +1935,10 @@ function FiberNode(tag, pendingProps, key, internalContextTag) { this.expirationTime = 0; this.alternate = null; } -function createFiber(tag, pendingProps, key, internalContextTag) { - return new FiberNode(tag, pendingProps, key, internalContextTag); -} function createWorkInProgress(current, pendingProps, expirationTime) { var workInProgress = current.alternate; null === workInProgress - ? ((workInProgress = createFiber( + ? ((workInProgress = new FiberNode( current.tag, pendingProps, current.key, @@ -1874,31 +1964,70 @@ function createWorkInProgress(current, pendingProps, expirationTime) { return workInProgress; } function createFiberFromElement(element, internalContextTag, expirationTime) { - var fiber = void 0, - type = element.type, + var type = element.type, key = element.key; element = element.props; - "function" === typeof type - ? ((fiber = - type.prototype && type.prototype.isReactComponent - ? createFiber(2, element, key, internalContextTag) - : createFiber(0, element, key, internalContextTag)), - (fiber.type = type)) - : "string" === typeof type - ? ((fiber = createFiber(5, element, key, internalContextTag)), - (fiber.type = type)) - : "object" === typeof type && - null !== type && - "number" === typeof type.tag - ? ((fiber = type), (fiber.pendingProps = element)) - : invariant( - !1, - "Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s", - null == type ? type : typeof type, - "" - ); - fiber.expirationTime = expirationTime; - return fiber; + var fiberTag = void 0; + if ("function" === typeof type) + fiberTag = type.prototype && type.prototype.isReactComponent ? 2 : 0; + else if ("string" === typeof type) fiberTag = 5; + else + switch (type) { + case REACT_FRAGMENT_TYPE: + return createFiberFromFragment( + element.children, + internalContextTag, + expirationTime, + key + ); + case REACT_STRICT_MODE_TYPE: + fiberTag = 11; + internalContextTag |= 2; + break; + case REACT_CALL_TYPE: + fiberTag = 7; + break; + case REACT_RETURN_TYPE: + fiberTag = 9; + break; + default: + if ("object" === typeof type && null !== type) + switch (type.$$typeof) { + case REACT_PROVIDER_TYPE: + fiberTag = 13; + break; + case REACT_CONTEXT_TYPE: + fiberTag = 12; + break; + default: + if ("number" === typeof type.tag) + return ( + (internalContextTag = type), + (internalContextTag.pendingProps = element), + (internalContextTag.expirationTime = expirationTime), + internalContextTag + ); + throwOnInvalidElementType(type, null); + } + else throwOnInvalidElementType(type, null); + } + internalContextTag = new FiberNode( + fiberTag, + element, + key, + internalContextTag + ); + internalContextTag.type = type; + internalContextTag.expirationTime = expirationTime; + return internalContextTag; +} +function throwOnInvalidElementType(type) { + invariant( + !1, + "Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s", + null == type ? type : typeof type, + "" + ); } function createFiberFromFragment( elements, @@ -1906,28 +2035,17 @@ function createFiberFromFragment( expirationTime, key ) { - elements = createFiber(10, elements, key, internalContextTag); + elements = new FiberNode(10, elements, key, internalContextTag); elements.expirationTime = expirationTime; return elements; } function createFiberFromText(content, internalContextTag, expirationTime) { - content = createFiber(6, content, null, internalContextTag); + content = new FiberNode(6, content, null, internalContextTag); content.expirationTime = expirationTime; return content; } -function createFiberFromCall(call, internalContextTag, expirationTime) { - internalContextTag = createFiber(7, call, call.key, internalContextTag); - internalContextTag.type = call.handler; - internalContextTag.expirationTime = expirationTime; - return internalContextTag; -} -function createFiberFromReturn(returnNode, internalContextTag, expirationTime) { - returnNode = createFiber(9, null, null, internalContextTag); - returnNode.expirationTime = expirationTime; - return returnNode; -} function createFiberFromPortal(portal, internalContextTag, expirationTime) { - internalContextTag = createFiber( + internalContextTag = new FiberNode( 4, null !== portal.children ? portal.children : [], portal.key, @@ -2012,8 +2130,7 @@ function insertUpdateIntoFiber(fiber, update) { function getStateFromUpdate(update, instance, prevState, props) { update = update.partialState; return "function" === typeof update - ? (debugRenderPhaseSideEffects && update.call(instance, prevState, props), - update.call(instance, prevState, props)) + ? update.call(instance, prevState, props) : update; } function processUpdateQueue( @@ -2058,6 +2175,10 @@ function processUpdateQueue( didSkip || ((queue.first = update.next), null === queue.first && (queue.last = null)); + (debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.internalContextTag & 2)) && + getStateFromUpdate(update, instance, current, props); if (update.isReplace) (current = getStateFromUpdate(update, instance, current, props)), (dontMutatePrevState = !0); @@ -2120,6 +2241,25 @@ function ReactFiberClassComponent( workInProgress.stateNode = instance; instance._reactInternalFiber = workInProgress; } + function callGetDerivedStateFromProps(workInProgress, instance, props) { + instance = workInProgress.type; + if ("function" === typeof instance.getDerivedStateFromProps) + return ( + (debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.internalContextTag & 2)) && + instance.getDerivedStateFromProps.call( + null, + props, + workInProgress.memoizedState + ), + instance.getDerivedStateFromProps.call( + null, + props, + workInProgress.memoizedState + ) + ); + } var updater = { isMounted: isMounted, enqueueSetState: function(instance, partialState, callback) { @@ -2170,6 +2310,7 @@ function ReactFiberClassComponent( }; return { adoptClassInstance: adoptClassInstance, + callGetDerivedStateFromProps: callGetDerivedStateFromProps, constructClassInstance: function(workInProgress, props) { var ctor = workInProgress.type, unmaskedContext = getUnmaskedContext(workInProgress), @@ -2178,40 +2319,58 @@ function ReactFiberClassComponent( context = needsContext ? getMaskedContext(workInProgress, unmaskedContext) : emptyObject; - props = new ctor(props, context); - adoptClassInstance(workInProgress, props); + (debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.internalContextTag & 2)) && + new ctor(props, context); + ctor = new ctor(props, context); + var state = + null !== ctor.state && void 0 !== ctor.state ? ctor.state : null; + adoptClassInstance(workInProgress, ctor); + workInProgress.memoizedState = state; + props = callGetDerivedStateFromProps(workInProgress, ctor, props); + null !== props && + void 0 !== props && + (workInProgress.memoizedState = Object.assign( + {}, + workInProgress.memoizedState, + props + )); needsContext && ((workInProgress = workInProgress.stateNode), (workInProgress.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext), (workInProgress.__reactInternalMemoizedMaskedChildContext = context)); - return props; + return ctor; }, mountClassInstance: function(workInProgress, renderExpirationTime) { var current = workInProgress.alternate, instance = workInProgress.stateNode, - state = instance.state || null, props = workInProgress.pendingProps, unmaskedContext = getUnmaskedContext(workInProgress); instance.props = props; - instance.state = workInProgress.memoizedState = state; + instance.state = workInProgress.memoizedState; instance.refs = emptyObject; instance.context = getMaskedContext(workInProgress, unmaskedContext); null != workInProgress.type && null != workInProgress.type.prototype && !0 === workInProgress.type.prototype.unstable_isAsyncReactComponent && - (workInProgress.internalContextTag |= 1); - "function" === typeof instance.componentWillMount && - ((state = instance.state), - instance.componentWillMount(), - debugRenderPhaseSideEffects && instance.componentWillMount(), - state !== instance.state && + ((workInProgress.internalContextTag |= 1), + (workInProgress.internalContextTag |= 2)); + ("function" !== typeof instance.UNSAFE_componentWillMount && + "function" !== typeof instance.componentWillMount) || + "function" === typeof workInProgress.type.getDerivedStateFromProps || + ((unmaskedContext = instance.state), + "function" === typeof instance.componentWillMount + ? instance.componentWillMount() + : instance.UNSAFE_componentWillMount(), + unmaskedContext !== instance.state && updater.enqueueReplaceState(instance, instance.state, null), - (state = workInProgress.updateQueue), - null !== state && + (unmaskedContext = workInProgress.updateQueue), + null !== unmaskedContext && (instance.state = processUpdateQueue( current, workInProgress, - state, + unmaskedContext, instance, props, renderExpirationTime @@ -2232,14 +2391,26 @@ function ReactFiberClassComponent( oldContext = instance.context, newUnmaskedContext = getUnmaskedContext(workInProgress); newUnmaskedContext = getMaskedContext(workInProgress, newUnmaskedContext); - "function" !== typeof instance.componentWillReceiveProps || + ("function" !== typeof instance.UNSAFE_componentWillReceiveProps && + "function" !== typeof instance.componentWillReceiveProps) || + "function" === typeof workInProgress.type.getDerivedStateFromProps || (oldProps === newProps && oldContext === newUnmaskedContext) || ((oldContext = instance.state), - instance.componentWillReceiveProps(newProps, newUnmaskedContext), - debugRenderPhaseSideEffects && - instance.componentWillReceiveProps(newProps, newUnmaskedContext), + "function" === typeof instance.componentWillReceiveProps + ? instance.componentWillReceiveProps(newProps, newUnmaskedContext) + : instance.UNSAFE_componentWillReceiveProps( + newProps, + newUnmaskedContext + ), instance.state !== oldContext && updater.enqueueReplaceState(instance, instance.state, null)); + var partialState = void 0; + oldProps !== newProps && + (partialState = callGetDerivedStateFromProps( + workInProgress, + instance, + newProps + )); oldContext = workInProgress.memoizedState; renderExpirationTime = null !== workInProgress.updateQueue @@ -2252,6 +2423,12 @@ function ReactFiberClassComponent( renderExpirationTime ) : oldContext; + null !== partialState && + void 0 !== partialState && + (renderExpirationTime = + null === renderExpirationTime || void 0 === renderExpirationTime + ? partialState + : Object.assign({}, renderExpirationTime, partialState)); if ( !( oldProps !== newProps || @@ -2268,47 +2445,44 @@ function ReactFiberClassComponent( (workInProgress.effectTag |= 4), !1 ); + partialState = renderExpirationTime; if ( null === oldProps || (null !== workInProgress.updateQueue && workInProgress.updateQueue.hasForceUpdate) ) - var shouldUpdate = !0; + partialState = !0; else { - shouldUpdate = workInProgress.stateNode; - var type = workInProgress.type; - "function" === typeof shouldUpdate.shouldComponentUpdate - ? ((type = shouldUpdate.shouldComponentUpdate( - newProps, - renderExpirationTime, - newUnmaskedContext - )), - debugRenderPhaseSideEffects && - shouldUpdate.shouldComponentUpdate( + var instance$jscomp$0 = workInProgress.stateNode, + type = workInProgress.type; + partialState = + "function" === typeof instance$jscomp$0.shouldComponentUpdate + ? instance$jscomp$0.shouldComponentUpdate( newProps, - renderExpirationTime, + partialState, newUnmaskedContext - ), - (shouldUpdate = type)) - : (shouldUpdate = - type.prototype && type.prototype.isPureReactComponent - ? !shallowEqual(oldProps, newProps) || - !shallowEqual(oldContext, renderExpirationTime) - : !0); + ) + : type.prototype && type.prototype.isPureReactComponent + ? !shallowEqual(oldProps, newProps) || + !shallowEqual(oldContext, partialState) + : !0; } - shouldUpdate - ? ("function" === typeof instance.componentWillUpdate && - (instance.componentWillUpdate( - newProps, - renderExpirationTime, - newUnmaskedContext - ), - debugRenderPhaseSideEffects && - instance.componentWillUpdate( - newProps, - renderExpirationTime, - newUnmaskedContext - )), + partialState + ? (("function" !== typeof instance.UNSAFE_componentWillUpdate && + "function" !== typeof instance.componentWillUpdate) || + "function" === + typeof workInProgress.type.getDerivedStateFromProps || + ("function" === typeof instance.componentWillUpdate + ? instance.componentWillUpdate( + newProps, + renderExpirationTime, + newUnmaskedContext + ) + : instance.UNSAFE_componentWillUpdate( + newProps, + renderExpirationTime, + newUnmaskedContext + )), "function" === typeof instance.componentDidUpdate && (workInProgress.effectTag |= 4)) : ("function" !== typeof instance.componentDidUpdate || @@ -2320,7 +2494,7 @@ function ReactFiberClassComponent( instance.props = newProps; instance.state = renderExpirationTime; instance.context = newUnmaskedContext; - return shouldUpdate; + return partialState; } }; } @@ -2362,7 +2536,7 @@ function coerceRef(current, element) { ); invariant( element._owner, - "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).", + "Element ref was specified as a string (%s) but no owner was set. This could happen for one of the following reasons:\n1. You may be adding a ref to a functional component\n2. You may be adding a ref to a component that was not created inside a component's render method\n3. You have multiple copies of React loaded\nSee https://fb.me/react-refs-must-have-owner for more information.", mixedRef ); } @@ -2464,38 +2638,6 @@ function ChildReconciler(shouldTrackSideEffects) { expirationTime["return"] = returnFiber; return expirationTime; } - function updateCall(returnFiber, current, call, expirationTime) { - if (null === current || 7 !== current.tag) - return ( - (current = createFiberFromCall( - call, - returnFiber.internalContextTag, - expirationTime - )), - (current["return"] = returnFiber), - current - ); - current = useFiber(current, call, expirationTime); - current["return"] = returnFiber; - return current; - } - function updateReturn(returnFiber, current, returnNode, expirationTime) { - if (null === current || 9 !== current.tag) - return ( - (current = createFiberFromReturn( - returnNode, - returnFiber.internalContextTag, - expirationTime - )), - (current.type = returnNode.value), - (current["return"] = returnFiber), - current - ); - current = useFiber(current, null, expirationTime); - current.type = returnNode.value; - current["return"] = returnFiber; - return current; - } function updatePortal(returnFiber, current, portal, expirationTime) { if ( null === current || @@ -2546,43 +2688,13 @@ function ChildReconciler(shouldTrackSideEffects) { if ("object" === typeof newChild && null !== newChild) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: - if (newChild.type === REACT_FRAGMENT_TYPE) - return ( - (newChild = createFiberFromFragment( - newChild.props.children, - returnFiber.internalContextTag, - expirationTime, - newChild.key - )), - (newChild["return"] = returnFiber), - newChild - ); - expirationTime = createFiberFromElement( - newChild, - returnFiber.internalContextTag, - expirationTime - ); - expirationTime.ref = coerceRef(null, newChild); - expirationTime["return"] = returnFiber; - return expirationTime; - case REACT_CALL_TYPE: - return ( - (newChild = createFiberFromCall( - newChild, - returnFiber.internalContextTag, - expirationTime - )), - (newChild["return"] = returnFiber), - newChild - ); - case REACT_RETURN_TYPE: return ( - (expirationTime = createFiberFromReturn( + (expirationTime = createFiberFromElement( newChild, returnFiber.internalContextTag, expirationTime )), - (expirationTime.type = newChild.value), + (expirationTime.ref = coerceRef(null, newChild)), (expirationTime["return"] = returnFiber), expirationTime ); @@ -2632,14 +2744,6 @@ function ChildReconciler(shouldTrackSideEffects) { ) : updateElement(returnFiber, oldFiber, newChild, expirationTime) : null; - case REACT_CALL_TYPE: - return newChild.key === key - ? updateCall(returnFiber, oldFiber, newChild, expirationTime) - : null; - case REACT_RETURN_TYPE: - return null === key - ? updateReturn(returnFiber, oldFiber, newChild, expirationTime) - : null; case REACT_PORTAL_TYPE: return newChild.key === key ? updatePortal(returnFiber, oldFiber, newChild, expirationTime) @@ -2699,24 +2803,6 @@ function ChildReconciler(shouldTrackSideEffects) { expirationTime ) ); - case REACT_CALL_TYPE: - return ( - (existingChildren = - existingChildren.get( - null === newChild.key ? newIdx : newChild.key - ) || null), - updateCall(returnFiber, existingChildren, newChild, expirationTime) - ); - case REACT_RETURN_TYPE: - return ( - (existingChildren = existingChildren.get(newIdx) || null), - updateReturn( - returnFiber, - existingChildren, - newChild, - expirationTime - ) - ); case REACT_PORTAL_TYPE: return ( (existingChildren = @@ -2973,72 +3059,14 @@ function ChildReconciler(shouldTrackSideEffects) { )), (currentFirstChild["return"] = returnFiber), (returnFiber = currentFirstChild)) - : ((expirationTime = createFiberFromElement( - newChild, - returnFiber.internalContextTag, - expirationTime - )), - (expirationTime.ref = coerceRef(currentFirstChild, newChild)), - (expirationTime["return"] = returnFiber), - (returnFiber = expirationTime)); - } - return placeSingleChild(returnFiber); - case REACT_CALL_TYPE: - a: { - for (isObject = newChild.key; null !== currentFirstChild; ) { - if (currentFirstChild.key === isObject) - if (7 === currentFirstChild.tag) { - deleteRemainingChildren( - returnFiber, - currentFirstChild.sibling - ); - currentFirstChild = useFiber( - currentFirstChild, - newChild, - expirationTime - ); - currentFirstChild["return"] = returnFiber; - returnFiber = currentFirstChild; - break a; - } else { - deleteRemainingChildren(returnFiber, currentFirstChild); - break; - } - else deleteChild(returnFiber, currentFirstChild); - currentFirstChild = currentFirstChild.sibling; - } - currentFirstChild = createFiberFromCall( - newChild, - returnFiber.internalContextTag, - expirationTime - ); - currentFirstChild["return"] = returnFiber; - returnFiber = currentFirstChild; - } - return placeSingleChild(returnFiber); - case REACT_RETURN_TYPE: - a: { - if (null !== currentFirstChild) - if (9 === currentFirstChild.tag) { - deleteRemainingChildren(returnFiber, currentFirstChild.sibling); - currentFirstChild = useFiber( - currentFirstChild, - null, + : ((expirationTime = createFiberFromElement( + newChild, + returnFiber.internalContextTag, expirationTime - ); - currentFirstChild.type = newChild.value; - currentFirstChild["return"] = returnFiber; - returnFiber = currentFirstChild; - break a; - } else deleteRemainingChildren(returnFiber, currentFirstChild); - currentFirstChild = createFiberFromReturn( - newChild, - returnFiber.internalContextTag, - expirationTime - ); - currentFirstChild.type = newChild.value; - currentFirstChild["return"] = returnFiber; - returnFiber = currentFirstChild; + )), + (expirationTime.ref = coerceRef(currentFirstChild, newChild)), + (expirationTime["return"] = returnFiber), + (returnFiber = expirationTime)); } return placeSingleChild(returnFiber); case REACT_PORTAL_TYPE: @@ -3131,7 +3159,22 @@ function ChildReconciler(shouldTrackSideEffects) { }; } var reconcileChildFibers = ChildReconciler(!0), - mountChildFibers = ChildReconciler(!1); + mountChildFibers = ChildReconciler(!1), + stack = [], + index$1 = -1; +function popProvider(providerFiber) { + stack[index$1] = null; + --index$1; + providerFiber = providerFiber.type.context; + if (0 > index$1) + (providerFiber.currentValue = providerFiber.defaultValue), + (providerFiber.changedBits = 0); + else { + var previousProviderFiber = stack[index$1]; + providerFiber.currentValue = previousProviderFiber.pendingProps.value; + providerFiber.changedBits = previousProviderFiber.stateNode; + } +} function ReactFiberBeginWork( config, hostContext, @@ -3176,7 +3219,10 @@ function ReactFiberBeginWork( ); shouldUpdate = workInProgress.stateNode; ReactCurrentOwner.current = workInProgress; - debugRenderPhaseSideEffects && shouldUpdate.render(); + (debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.internalContextTag & 2)) && + shouldUpdate.render(); var nextChildren = shouldUpdate.render(); workInProgress.effectTag |= 1; reconcileChildren(current, workInProgress, nextChildren); @@ -3235,11 +3281,17 @@ function ReactFiberBeginWork( workInProgress, workInProgress.stateNode.containerInfo ); + break; + case 13: + (index$1 += 1), + (stack[index$1] = workInProgress), + (current = workInProgress.type.context), + (current.currentValue = workInProgress.pendingProps.value), + (current.changedBits = workInProgress.stateNode); } return null; } var shouldSetTextContent = config.shouldSetTextContent, - useSyncScheduling = config.useSyncScheduling, shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree, pushHostContext = hostContext.pushHostContext, pushHostContainer = hostContext.pushHostContainer, @@ -3258,6 +3310,7 @@ function ReactFiberBeginWork( } ); var adoptClassInstance = config.adoptClassInstance, + callGetDerivedStateFromProps = config.callGetDerivedStateFromProps, constructClassInstance = config.constructClassInstance, mountClassInstance = config.mountClassInstance, updateClassInstance = config.updateClassInstance; @@ -3282,8 +3335,25 @@ function ReactFiberBeginWork( workInProgress.effectTag |= 1; "object" === typeof fn && null !== fn && - "function" === typeof fn.render - ? ((workInProgress.tag = 2), + "function" === typeof fn.render && + void 0 === fn.$$typeof + ? ((unmaskedContext = workInProgress.type), + (workInProgress.tag = 2), + (workInProgress.memoizedState = + null !== fn.state && void 0 !== fn.state ? fn.state : null), + "function" === typeof unmaskedContext.getDerivedStateFromProps && + ((props = callGetDerivedStateFromProps( + workInProgress, + fn, + props + )), + null !== props && + void 0 !== props && + (workInProgress.memoizedState = Object.assign( + {}, + workInProgress.memoizedState, + props + ))), (props = pushContextProvider(workInProgress)), adoptClassInstance(workInProgress, fn), mountClassInstance(workInProgress, renderExpirationTime), @@ -3395,10 +3465,10 @@ function ReactFiberBeginWork( shouldSetTextContent(props, unmaskedContext) && (workInProgress.effectTag |= 16), markRef(current, workInProgress), - 2147483647 !== renderExpirationTime && - !useSyncScheduling && + 1073741823 !== renderExpirationTime && + workInProgress.internalContextTag & 1 && shouldDeprioritizeSubtree(props, fn) - ? ((workInProgress.expirationTime = 2147483647), + ? ((workInProgress.expirationTime = 1073741823), (current = null)) : (reconcileChildren(current, workInProgress, memoizedProps), (workInProgress.memoizedProps = fn), @@ -3431,7 +3501,7 @@ function ReactFiberBeginWork( ) : reconcileChildFibers( workInProgress, - workInProgress.stateNode, + current.stateNode, fn, renderExpirationTime )), @@ -3469,6 +3539,24 @@ function ReactFiberBeginWork( return ( (renderExpirationTime = workInProgress.pendingProps), didPerformWorkStackCursor.current || + workInProgress.memoizedProps !== renderExpirationTime + ? (reconcileChildren( + current, + workInProgress, + renderExpirationTime + ), + (workInProgress.memoizedProps = renderExpirationTime), + (current = workInProgress.child)) + : (current = bailoutOnAlreadyFinishedWork( + current, + workInProgress + )), + current + ); + case 11: + return ( + (renderExpirationTime = workInProgress.pendingProps.children), + didPerformWorkStackCursor.current || (null !== renderExpirationTime && workInProgress.memoizedProps !== renderExpirationTime) ? (reconcileChildren( @@ -3484,6 +3572,10 @@ function ReactFiberBeginWork( )), current ); + case 13: + return null; + case 12: + return null; default: invariant( !1, @@ -3611,7 +3703,8 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { type, oldProps, newProps, - renderExpirationTime + renderExpirationTime, + currentHostContext ); current.ref !== workInProgress.ref && (workInProgress.effectTag |= 128); @@ -3632,40 +3725,52 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { current ) && markUpdate(workInProgress); else { - current = createInstance( + oldProps = createInstance( type, newProps, renderExpirationTime, current, workInProgress ); - a: for (oldProps = workInProgress.child; null !== oldProps; ) { - if (5 === oldProps.tag || 6 === oldProps.tag) - appendInitialChild(current, oldProps.stateNode); - else if (4 !== oldProps.tag && null !== oldProps.child) { - oldProps.child["return"] = oldProps; - oldProps = oldProps.child; + a: for ( + currentHostContext = workInProgress.child; + null !== currentHostContext; + + ) { + if ( + 5 === currentHostContext.tag || + 6 === currentHostContext.tag + ) + appendInitialChild(oldProps, currentHostContext.stateNode); + else if ( + 4 !== currentHostContext.tag && + null !== currentHostContext.child + ) { + currentHostContext.child["return"] = currentHostContext; + currentHostContext = currentHostContext.child; continue; } - if (oldProps === workInProgress) break; - for (; null === oldProps.sibling; ) { + if (currentHostContext === workInProgress) break; + for (; null === currentHostContext.sibling; ) { if ( - null === oldProps["return"] || - oldProps["return"] === workInProgress + null === currentHostContext["return"] || + currentHostContext["return"] === workInProgress ) break a; - oldProps = oldProps["return"]; + currentHostContext = currentHostContext["return"]; } - oldProps.sibling["return"] = oldProps["return"]; - oldProps = oldProps.sibling; + currentHostContext.sibling["return"] = + currentHostContext["return"]; + currentHostContext = currentHostContext.sibling; } finalizeInitialChildren( - current, + oldProps, type, newProps, - renderExpirationTime + renderExpirationTime, + current ) && markUpdate(workInProgress); - workInProgress.stateNode = current; + workInProgress.stateNode = oldProps; } null !== workInProgress.ref && (workInProgress.effectTag |= 128); } @@ -3716,7 +3821,7 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { ) { if (5 === oldProps.tag || 6 === oldProps.tag || 4 === oldProps.tag) invariant(!1, "A call cannot have host component children."); - else if (9 === oldProps.tag) type.push(oldProps.type); + else if (9 === oldProps.tag) type.push(oldProps.pendingProps.value); else if (null !== oldProps.child) { oldProps.child["return"] = oldProps; oldProps = oldProps.child; @@ -3748,12 +3853,18 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { return null; case 10: return null; + case 11: + return null; case 4: return ( popHostContainer(workInProgress), updateHostContainer(workInProgress), null ); + case 13: + return popProvider(workInProgress), null; + case 12: + return null; case 0: invariant( !1, @@ -4063,11 +4174,18 @@ function ReactFiberCommitWork(config, captureError) { break; case 3: instance = finishedWork.updateQueue; - null !== instance && - commitCallbacks( - instance, - null !== finishedWork.child ? finishedWork.child.stateNode : null - ); + if (null !== instance) { + current = null; + if (null !== finishedWork.child) + switch (finishedWork.child.tag) { + case 5: + current = getPublicInstance(finishedWork.child.stateNode); + break; + case 2: + current = finishedWork.child.stateNode; + } + commitCallbacks(instance, current); + } break; case 5: instance = finishedWork.stateNode; @@ -4162,7 +4280,7 @@ function ReactFiberHostContext(config) { } function ReactFiberHydrationContext(config) { function deleteHydratableInstance(returnFiber, instance) { - var fiber = createFiber(5, null, null, 0); + var fiber = new FiberNode(5, null, null, 0); fiber.type = "DELETED"; fiber.stateNode = instance; fiber["return"] = returnFiber; @@ -4328,8 +4446,8 @@ function ReactFiberScheduler(config) { siblingFiber = workInProgress$jscomp$0.sibling; var workInProgress = workInProgress$jscomp$0; if ( - 2147483647 === nextRenderExpirationTime || - 2147483647 !== workInProgress.expirationTime + 1073741823 === nextRenderExpirationTime || + 1073741823 !== workInProgress.expirationTime ) { if (2 !== workInProgress.tag && 3 !== workInProgress.tag) var newExpirationTime = 0; @@ -4437,6 +4555,12 @@ function ReactFiberScheduler(config) { previousContext = emptyObject; contextStackCursor.current = emptyObject; didPerformWorkStackCursor.current = !1; + for (var i = index$1; -1 < i; i--) { + var context = stack[i].type.context; + context.currentValue = context.defaultValue; + context.changedBits = 0; + stack[i] = null; + } resetHostContainer(); nextRoot = root; nextRenderExpirationTime = expirationTime; @@ -4446,52 +4570,54 @@ function ReactFiberScheduler(config) { expirationTime ); } - var didError = !1, - error = null; + context = !1; + var error = null; try { workLoop(expirationTime); } catch (e) { - (didError = !0), (error = e); + (context = !0), (error = e); } - for (; didError; ) { + for (; context; ) { if (didFatal) { firstUncaughtError = error; break; } - var failedWork = nextUnitOfWork; - if (null === failedWork) didFatal = !0; + i = nextUnitOfWork; + if (null === i) didFatal = !0; else { - var boundary = captureError(failedWork, error); + var boundary = captureError(i, error); invariant( null !== boundary, "Should have found an error boundary. This error is likely caused by a bug in React. Please file an issue." ); if (!didFatal) { try { - didError = boundary; + context = boundary; error = expirationTime; - for (boundary = didError; null !== failedWork; ) { - switch (failedWork.tag) { + for (boundary = context; null !== i; ) { + switch (i.tag) { case 2: - popContextProvider(failedWork); + popContextProvider(i); break; case 5: - popHostContext(failedWork); + popHostContext(i); break; case 3: - popHostContainer(failedWork); + popHostContainer(i); break; case 4: - popHostContainer(failedWork); + popHostContainer(i); + break; + case 13: + popProvider(i); } - if (failedWork === boundary || failedWork.alternate === boundary) - break; - failedWork = failedWork["return"]; + if (i === boundary || i.alternate === boundary) break; + i = i["return"]; } - nextUnitOfWork = performFailedUnitOfWork(didError); + nextUnitOfWork = performFailedUnitOfWork(context); workLoop(error); } catch (e) { - didError = !0; + context = !0; error = e; continue; } @@ -4587,10 +4713,24 @@ function ReactFiberScheduler(config) { }; capturedErrors.set(boundary, error$jscomp$0); try { - if (!1 !== showDialog(error$jscomp$0)) { - var error = error$jscomp$0.error; - (error && error.suppressReactErrorLogging) || console.error(error); - } + var componentStack = error$jscomp$0.componentStack, + error = error$jscomp$0.error; + if (error instanceof Error) { + var message = error.message, + name = error.name; + var errorToHandle = error; + try { + errorToHandle.message = + (message ? name + ": " + message : name) + + "\n\nThis error is located at:" + + componentStack; + } catch (e) {} + } else + errorToHandle = + "string" === typeof error + ? Error(error + "\n\nThis error is located at:" + componentStack) + : Error("Unspecified error at:" + componentStack); + ExceptionsManager.handleException(errorToHandle, !1); } catch (e) { (e && e.suppressReactErrorLogging) || console.error(e); } @@ -4626,9 +4766,7 @@ function ReactFiberScheduler(config) { ? expirationContext : isWorking ? isCommitting ? 1 : nextRenderExpirationTime - : !useSyncScheduling || fiber.internalContextTag & 1 - ? computeAsyncExpiration() - : 1; + : fiber.internalContextTag & 1 ? computeAsyncExpiration() : 1; } function scheduleWork(fiber, expirationTime) { return scheduleWorkImpl(fiber, expirationTime, !1); @@ -5016,7 +5154,6 @@ function ReactFiberScheduler(config) { now = config.now, scheduleDeferredCallback = config.scheduleDeferredCallback, cancelDeferredCallback = config.cancelDeferredCallback, - useSyncScheduling = config.useSyncScheduling, prepareForCommit = config.prepareForCommit, resetAfterCommit = config.resetAfterCommit, startTime = now(), @@ -5053,7 +5190,6 @@ function ReactFiberScheduler(config) { nestedUpdateCount = 0, timeHeuristicForUnitOfWork = 1; return { - computeAsyncExpiration: computeAsyncExpiration, computeExpirationForFiber: computeExpirationForFiber, scheduleWork: scheduleWork, requestWork: requestWork, @@ -5186,14 +5322,13 @@ function ReactFiberReconciler$1(config) { } var getPublicInstance = config.getPublicInstance; config = ReactFiberScheduler(config); - var computeAsyncExpiration = config.computeAsyncExpiration, - computeExpirationForFiber = config.computeExpirationForFiber, + var computeExpirationForFiber = config.computeExpirationForFiber, scheduleWork = config.scheduleWork; return { - createContainer: function(containerInfo, hydrate) { - var uninitializedFiber = createFiber(3, null, 0); + createContainer: function(containerInfo, isAsync, hydrate) { + isAsync = new FiberNode(3, null, null, isAsync ? 3 : 0); containerInfo = { - current: uninitializedFiber, + current: isAsync, containerInfo: containerInfo, pendingChildren: null, remainingExpirationTime: 0, @@ -5205,22 +5340,15 @@ function ReactFiberReconciler$1(config) { firstBatch: null, nextScheduledRoot: null }; - return (uninitializedFiber.stateNode = containerInfo); + return (isAsync.stateNode = containerInfo); }, updateContainer: function(element, container, parentComponent, callback) { - var current = container.current; - current = - null != element && - null != element.type && - null != element.type.prototype && - !0 === element.type.prototype.unstable_isAsyncReactComponent - ? computeAsyncExpiration() - : computeExpirationForFiber(current); + var expirationTime = computeExpirationForFiber(container.current); return updateContainerAtExpirationTime( element, container, parentComponent, - current, + expirationTime, callback ); }, @@ -5356,463 +5484,321 @@ function recursivelyUncacheFiberNode(node) { node._children.forEach(recursivelyUncacheFiberNode)); } var NativeRenderer = reactReconciler({ - appendInitialChild: function(parentInstance, child) { - parentInstance._children.push(child); - }, - createInstance: function( - type, - props, - rootContainerInstance, - hostContext, - internalInstanceHandle - ) { - hostContext = ReactNativeTagHandles.allocateTag(); - if (viewConfigs.has(type)) var viewConfig = viewConfigs.get(type); - else - (viewConfig = viewConfigCallbacks.get(type)), - invariant( - "function" === typeof viewConfig, - "View config not found for name %s", - type - ), - viewConfigCallbacks.set(type, null), - (viewConfig = viewConfig()), - viewConfigs.set(type, viewConfig); - invariant(viewConfig, "View config not found for name %s", type); - type = viewConfig; - viewConfig = diffProperties( - null, - emptyObject$1, + appendInitialChild: function(parentInstance, child) { + parentInstance._children.push(child); + }, + createInstance: function( + type, props, - type.validAttributes - ); - UIManager.createView( + rootContainerInstance, hostContext, - type.uiViewClassName, + internalInstanceHandle + ) { + hostContext = ReactNativeTagHandles.allocateTag(); + if (viewConfigs.has(type)) var viewConfig = viewConfigs.get(type); + else + (viewConfig = viewConfigCallbacks.get(type)), + invariant( + "function" === typeof viewConfig, + "View config not found for name %s", + type + ), + viewConfigCallbacks.set(type, null), + (viewConfig = viewConfig()), + viewConfigs.set(type, viewConfig); + invariant(viewConfig, "View config not found for name %s", type); + type = viewConfig; + viewConfig = diffProperties( + null, + emptyObject$1, + props, + type.validAttributes + ); + UIManager.createView( + hostContext, + type.uiViewClassName, + rootContainerInstance, + viewConfig + ); + rootContainerInstance = new ReactNativeFiberHostComponent( + hostContext, + type + ); + instanceCache[hostContext] = internalInstanceHandle; + instanceProps[hostContext] = props; + return rootContainerInstance; + }, + createTextInstance: function( + text, rootContainerInstance, - viewConfig - ); - rootContainerInstance = new ReactNativeFiberHostComponent( hostContext, - type - ); - instanceCache[hostContext] = internalInstanceHandle; - instanceProps[hostContext] = props; - return rootContainerInstance; - }, - createTextInstance: function( - text, - rootContainerInstance, - hostContext, - internalInstanceHandle - ) { - hostContext = ReactNativeTagHandles.allocateTag(); - UIManager.createView(hostContext, "RCTRawText", rootContainerInstance, { - text: text - }); - instanceCache[hostContext] = internalInstanceHandle; - return hostContext; - }, - finalizeInitialChildren: function(parentInstance) { - if (0 === parentInstance._children.length) return !1; - var nativeTags = parentInstance._children.map(function(child) { - return "number" === typeof child ? child : child._nativeTag; - }); - UIManager.setChildren(parentInstance._nativeTag, nativeTags); - return !1; - }, - getRootHostContext: function() { - return emptyObject; - }, - getChildHostContext: function() { - return emptyObject; - }, - getPublicInstance: function(instance) { - return instance; - }, - now: now, - prepareForCommit: function() {}, - prepareUpdate: function() { - return emptyObject; - }, - resetAfterCommit: function() {}, - scheduleDeferredCallback: function(callback) { - scheduledCallback = callback; - return setTimeout(setTimeoutCallback, 1); - }, - cancelDeferredCallback: function(callbackID) { - scheduledCallback = null; - clearTimeout(callbackID); - }, - shouldDeprioritizeSubtree: function() { - return !1; - }, - shouldSetTextContent: function() { - return !1; - }, - useSyncScheduling: !0, - mutation: { - appendChild: function(parentInstance, child) { - var childTag = "number" === typeof child ? child : child._nativeTag, - children = parentInstance._children, - index = children.indexOf(child); - 0 <= index - ? (children.splice(index, 1), - children.push(child), - UIManager.manageChildren( - parentInstance._nativeTag, - [index], - [children.length - 1], - [], - [], - [] - )) - : (children.push(child), - UIManager.manageChildren( - parentInstance._nativeTag, - [], - [], - [childTag], - [children.length - 1], - [] - )); + internalInstanceHandle + ) { + hostContext = ReactNativeTagHandles.allocateTag(); + UIManager.createView(hostContext, "RCTRawText", rootContainerInstance, { + text: text + }); + instanceCache[hostContext] = internalInstanceHandle; + return hostContext; }, - appendChildToContainer: function(parentInstance, child) { - UIManager.setChildren(parentInstance, [ - "number" === typeof child ? child : child._nativeTag - ]); + finalizeInitialChildren: function(parentInstance) { + if (0 === parentInstance._children.length) return !1; + var nativeTags = parentInstance._children.map(function(child) { + return "number" === typeof child ? child : child._nativeTag; + }); + UIManager.setChildren(parentInstance._nativeTag, nativeTags); + return !1; }, - commitTextUpdate: function(textInstance, oldText, newText) { - UIManager.updateView(textInstance, "RCTRawText", { text: newText }); + getRootHostContext: function() { + return emptyObject; }, - commitMount: function() {}, - commitUpdate: function( - instance, - updatePayloadTODO, - type, - oldProps, - newProps - ) { - updatePayloadTODO = instance.viewConfig; - instanceProps[instance._nativeTag] = newProps; - oldProps = diffProperties( - null, - oldProps, - newProps, - updatePayloadTODO.validAttributes - ); - null != oldProps && - UIManager.updateView( - instance._nativeTag, - updatePayloadTODO.uiViewClassName, - oldProps - ); + getChildHostContext: function() { + return emptyObject; }, - insertBefore: function(parentInstance, child, beforeChild) { - var children = parentInstance._children, - index = children.indexOf(child); - 0 <= index - ? (children.splice(index, 1), - (beforeChild = children.indexOf(beforeChild)), - children.splice(beforeChild, 0, child), - UIManager.manageChildren( - parentInstance._nativeTag, - [index], - [beforeChild], - [], - [], - [] - )) - : ((index = children.indexOf(beforeChild)), - children.splice(index, 0, child), - UIManager.manageChildren( - parentInstance._nativeTag, - [], - [], - ["number" === typeof child ? child : child._nativeTag], - [index], - [] - )); + getPublicInstance: function(instance) { + return instance; }, - insertInContainerBefore: function(parentInstance) { - invariant( - "number" !== typeof parentInstance, - "Container does not support insertBefore operation" - ); + now: now, + prepareForCommit: function() {}, + prepareUpdate: function() { + return emptyObject; }, - removeChild: function(parentInstance, child) { - recursivelyUncacheFiberNode(child); - var children = parentInstance._children; - child = children.indexOf(child); - children.splice(child, 1); - UIManager.manageChildren( - parentInstance._nativeTag, - [], - [], - [], - [], - [child] - ); + resetAfterCommit: function() {}, + scheduleDeferredCallback: function(callback) { + scheduledCallback = callback; + return setTimeout(setTimeoutCallback, 1); }, - removeChildFromContainer: function(parentInstance, child) { - recursivelyUncacheFiberNode(child); - UIManager.manageChildren(parentInstance, [], [], [], [], [0]); + cancelDeferredCallback: function(callbackID) { + scheduledCallback = null; + clearTimeout(callbackID); }, - resetTextContent: function() {} - } -}); -function findNodeHandle(componentOrHandle) { - if (null == componentOrHandle) return null; - if ("number" === typeof componentOrHandle) return componentOrHandle; - var internalInstance = componentOrHandle._reactInternalFiber; - if (internalInstance) - return NativeRenderer.findHostInstance(internalInstance); - if (componentOrHandle) return componentOrHandle; - invariant( - ("object" === typeof componentOrHandle && - "_nativeTag" in componentOrHandle) || - (null != componentOrHandle.render && - "function" === typeof componentOrHandle.render), - "findNodeHandle(...): Argument is not a component (type: %s, keys: %s)", - typeof componentOrHandle, - Object.keys(componentOrHandle) - ); - invariant( - !1, - "findNodeHandle(...): Unable to find node handle for unmounted component." - ); -} -function findNumericNodeHandleFiber(componentOrHandle) { - componentOrHandle = findNodeHandle(componentOrHandle); - return null == componentOrHandle || "number" === typeof componentOrHandle - ? componentOrHandle - : componentOrHandle._nativeTag; -} -function _inherits(subClass, superClass) { - if ("function" !== typeof superClass && null !== superClass) - throw new TypeError( - "Super expression must either be null or a function, not " + - typeof superClass - ); - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - enumerable: !1, - writable: !0, - configurable: !0 - } - }); - superClass && - (Object.setPrototypeOf - ? Object.setPrototypeOf(subClass, superClass) - : (subClass.__proto__ = superClass)); -} -var ReactNativeComponent = (function(_React$Component) { - function ReactNativeComponent() { - if (!(this instanceof ReactNativeComponent)) - throw new TypeError("Cannot call a class as a function"); - var call = _React$Component.apply(this, arguments); - if (!this) - throw new ReferenceError( - "this hasn't been initialised - super() hasn't been called" - ); - return !call || ("object" !== typeof call && "function" !== typeof call) - ? this - : call; - } - _inherits(ReactNativeComponent, _React$Component); - ReactNativeComponent.prototype.blur = function() { - TextInputState.blurTextInput(findNumericNodeHandleFiber(this)); - }; - ReactNativeComponent.prototype.focus = function() { - TextInputState.focusTextInput(findNumericNodeHandleFiber(this)); - }; - ReactNativeComponent.prototype.measure = function(callback) { - UIManager.measure( - findNumericNodeHandleFiber(this), - mountSafeCallback(this, callback) - ); - }; - ReactNativeComponent.prototype.measureInWindow = function(callback) { - UIManager.measureInWindow( - findNumericNodeHandleFiber(this), - mountSafeCallback(this, callback) - ); - }; - ReactNativeComponent.prototype.measureLayout = function( - relativeToNativeNode, - onSuccess, - onFail - ) { - UIManager.measureLayout( - findNumericNodeHandleFiber(this), - relativeToNativeNode, - mountSafeCallback(this, onFail), - mountSafeCallback(this, onSuccess) - ); - }; - ReactNativeComponent.prototype.setNativeProps = function(nativeProps) { - var maybeInstance = void 0; - try { - maybeInstance = findNodeHandle(this); - } catch (error) {} - if (null != maybeInstance) { - var viewConfig = maybeInstance.viewConfig; - nativeProps = diffProperties( + shouldDeprioritizeSubtree: function() { + return !1; + }, + shouldSetTextContent: function() { + return !1; + }, + mutation: { + appendChild: function(parentInstance, child) { + var childTag = "number" === typeof child ? child : child._nativeTag, + children = parentInstance._children, + index = children.indexOf(child); + 0 <= index + ? (children.splice(index, 1), + children.push(child), + UIManager.manageChildren( + parentInstance._nativeTag, + [index], + [children.length - 1], + [], + [], + [] + )) + : (children.push(child), + UIManager.manageChildren( + parentInstance._nativeTag, + [], + [], + [childTag], + [children.length - 1], + [] + )); + }, + appendChildToContainer: function(parentInstance, child) { + UIManager.setChildren(parentInstance, [ + "number" === typeof child ? child : child._nativeTag + ]); + }, + commitTextUpdate: function(textInstance, oldText, newText) { + UIManager.updateView(textInstance, "RCTRawText", { text: newText }); + }, + commitMount: function() {}, + commitUpdate: function( + instance, + updatePayloadTODO, + type, + oldProps, + newProps + ) { + updatePayloadTODO = instance.viewConfig; + instanceProps[instance._nativeTag] = newProps; + oldProps = diffProperties( null, - emptyObject$1, - nativeProps, - viewConfig.validAttributes + oldProps, + newProps, + updatePayloadTODO.validAttributes ); - null != nativeProps && + null != oldProps && UIManager.updateView( - maybeInstance._nativeTag, - viewConfig.uiViewClassName, - nativeProps + instance._nativeTag, + updatePayloadTODO.uiViewClassName, + oldProps ); - } - }; - return ReactNativeComponent; - })(React.Component), - getInspectorDataForViewTag = void 0; -getInspectorDataForViewTag = function() { - invariant(!1, "getInspectorDataForViewTag() is not available in production"); -}; -fiberBatchedUpdates = NativeRenderer.batchedUpdates; -var roots = new Map(); -function fn$jscomp$inline_616(capturedError) { - var componentStack = capturedError.componentStack, - error = capturedError.error; - if (error instanceof Error) { - capturedError = error.message; - var name = error.name; - try { - error.message = - (capturedError ? name + ": " + capturedError : name) + - "\n\nThis error is located at:" + - componentStack; - } catch (e) {} - } else - error = - "string" === typeof error - ? Error(error + "\n\nThis error is located at:" + componentStack) - : Error("Unspecified error at:" + componentStack); - ExceptionsManager.handleException(error, !1); - return !1; -} -invariant( - showDialog === defaultShowDialog, - "The custom dialog was already injected." -); -invariant( - "function" === typeof fn$jscomp$inline_616, - "Injected showDialog() must be a function." -); -showDialog = fn$jscomp$inline_616; -var ReactNativeRenderer = { - NativeComponent: ReactNativeComponent, - findNodeHandle: findNumericNodeHandleFiber, - render: function(element, containerTag, callback) { - var root = roots.get(containerTag); - root || - ((root = NativeRenderer.createContainer(containerTag, !1)), - roots.set(containerTag, root)); - NativeRenderer.updateContainer(element, root, null, callback); - return NativeRenderer.getPublicRootInstance(root); - }, - unmountComponentAtNode: function(containerTag) { - var root = roots.get(containerTag); - root && - NativeRenderer.updateContainer(null, root, null, function() { - roots["delete"](containerTag); - }); - }, - unmountComponentAtNodeAndRemoveContainer: function(containerTag) { - ReactNativeRenderer.unmountComponentAtNode(containerTag); - UIManager.removeRootView(containerTag); - }, - createPortal: function(children, containerTag) { - return createPortal( - children, - containerTag, - null, - 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null - ); - }, - unstable_batchedUpdates: batchedUpdates, - flushSync: NativeRenderer.flushSync, - __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { - NativeMethodsMixin: { - measure: function(callback) { - UIManager.measure( - findNumericNodeHandleFiber(this), - mountSafeCallback(this, callback) - ); }, - measureInWindow: function(callback) { - UIManager.measureInWindow( - findNumericNodeHandleFiber(this), - mountSafeCallback(this, callback) + insertBefore: function(parentInstance, child, beforeChild) { + var children = parentInstance._children, + index = children.indexOf(child); + 0 <= index + ? (children.splice(index, 1), + (beforeChild = children.indexOf(beforeChild)), + children.splice(beforeChild, 0, child), + UIManager.manageChildren( + parentInstance._nativeTag, + [index], + [beforeChild], + [], + [], + [] + )) + : ((index = children.indexOf(beforeChild)), + children.splice(index, 0, child), + UIManager.manageChildren( + parentInstance._nativeTag, + [], + [], + ["number" === typeof child ? child : child._nativeTag], + [index], + [] + )); + }, + insertInContainerBefore: function(parentInstance) { + invariant( + "number" !== typeof parentInstance, + "Container does not support insertBefore operation" ); }, - measureLayout: function(relativeToNativeNode, onSuccess, onFail) { - UIManager.measureLayout( - findNumericNodeHandleFiber(this), - relativeToNativeNode, - mountSafeCallback(this, onFail), - mountSafeCallback(this, onSuccess) + removeChild: function(parentInstance, child) { + recursivelyUncacheFiberNode(child); + var children = parentInstance._children; + child = children.indexOf(child); + children.splice(child, 1); + UIManager.manageChildren( + parentInstance._nativeTag, + [], + [], + [], + [], + [child] ); }, - setNativeProps: function(nativeProps) { - var maybeInstance = void 0; - try { - maybeInstance = findNodeHandle(this); - } catch (error) {} - if (null != maybeInstance) { - var viewConfig = maybeInstance.viewConfig; - nativeProps = diffProperties( - null, - emptyObject$1, - nativeProps, - viewConfig.validAttributes + removeChildFromContainer: function(parentInstance, child) { + recursivelyUncacheFiberNode(child); + UIManager.manageChildren(parentInstance, [], [], [], [], [0]); + }, + resetTextContent: function() {} + } + }), + getInspectorDataForViewTag = void 0; +getInspectorDataForViewTag = function() { + invariant(!1, "getInspectorDataForViewTag() is not available in production"); +}; +findHostInstance = NativeRenderer.findHostInstance; +fiberBatchedUpdates = NativeRenderer.batchedUpdates; +var roots = new Map(), + ReactNativeRenderer = { + NativeComponent: ReactNativeComponent, + findNodeHandle: findNumericNodeHandleFiber, + render: function(element, containerTag, callback) { + var root = roots.get(containerTag); + root || + ((root = NativeRenderer.createContainer(containerTag, !1, !1)), + roots.set(containerTag, root)); + NativeRenderer.updateContainer(element, root, null, callback); + return NativeRenderer.getPublicRootInstance(root); + }, + unmountComponentAtNode: function(containerTag) { + var root = roots.get(containerTag); + root && + NativeRenderer.updateContainer(null, root, null, function() { + roots["delete"](containerTag); + }); + }, + unmountComponentAtNodeAndRemoveContainer: function(containerTag) { + ReactNativeRenderer.unmountComponentAtNode(containerTag); + UIManager.removeRootView(containerTag); + }, + createPortal: function(children, containerTag) { + return createPortal( + children, + containerTag, + null, + 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null + ); + }, + unstable_batchedUpdates: batchedUpdates, + flushSync: NativeRenderer.flushSync, + __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { + NativeMethodsMixin: { + measure: function(callback) { + UIManager.measure( + findNumericNodeHandleFiber(this), + mountSafeCallback(this, callback) + ); + }, + measureInWindow: function(callback) { + UIManager.measureInWindow( + findNumericNodeHandleFiber(this), + mountSafeCallback(this, callback) + ); + }, + measureLayout: function(relativeToNativeNode, onSuccess, onFail) { + UIManager.measureLayout( + findNumericNodeHandleFiber(this), + relativeToNativeNode, + mountSafeCallback(this, onFail), + mountSafeCallback(this, onSuccess) ); - null != nativeProps && - UIManager.updateView( - maybeInstance._nativeTag, - viewConfig.uiViewClassName, - nativeProps + }, + setNativeProps: function(nativeProps) { + var maybeInstance = void 0; + try { + maybeInstance = findNodeHandle(this); + } catch (error) {} + if (null != maybeInstance) { + var viewConfig = maybeInstance.viewConfig; + nativeProps = diffProperties( + null, + emptyObject$1, + nativeProps, + viewConfig.validAttributes ); + null != nativeProps && + UIManager.updateView( + maybeInstance._nativeTag, + viewConfig.uiViewClassName, + nativeProps + ); + } + }, + focus: function() { + TextInputState.focusTextInput(findNumericNodeHandleFiber(this)); + }, + blur: function() { + TextInputState.blurTextInput(findNumericNodeHandleFiber(this)); } }, - focus: function() { - TextInputState.focusTextInput(findNumericNodeHandleFiber(this)); + ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin, + ReactGlobalSharedState: ReactGlobalSharedState, + ReactNativeComponentTree: ReactNativeComponentTree, + ReactNativePropRegistry: ReactNativePropRegistry, + TouchHistoryMath: TouchHistoryMath, + createReactNativeComponentClass: function(name, callback) { + invariant( + !viewConfigCallbacks.has(name), + "Tried to register two views with the same name %s", + name + ); + viewConfigCallbacks.set(name, callback); + return name; }, - blur: function() { - TextInputState.blurTextInput(findNumericNodeHandleFiber(this)); + takeSnapshot: function(view, options) { + "number" !== typeof view && + "window" !== view && + (view = findNumericNodeHandleFiber(view) || "window"); + return UIManager.__takeSnapshot(view, options); } - }, - ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin, - ReactGlobalSharedState: ReactGlobalSharedState, - ReactNativeComponentTree: ReactNativeComponentTree, - ReactNativePropRegistry: ReactNativePropRegistry, - TouchHistoryMath: TouchHistoryMath, - createReactNativeComponentClass: function(name, callback) { - invariant( - !viewConfigCallbacks.has(name), - "Tried to register two views with the same name %s", - name - ); - viewConfigCallbacks.set(name, callback); - return name; - }, - takeSnapshot: function(view, options) { - "number" !== typeof view && - "window" !== view && - (view = findNumericNodeHandleFiber(view) || "window"); - return UIManager.__takeSnapshot(view, options); } - } -}; + }; NativeRenderer.injectIntoDevTools({ findFiberByHostInstance: getInstanceFromTag, getInspectorDataForViewTag: getInspectorDataForViewTag, diff --git a/Libraries/Renderer/shims/ReactFeatureFlags.js b/Libraries/Renderer/shims/ReactFeatureFlags.js index 276dc914254c55..ac1f582d08db94 100644 --- a/Libraries/Renderer/shims/ReactFeatureFlags.js +++ b/Libraries/Renderer/shims/ReactFeatureFlags.js @@ -9,8 +9,12 @@ 'use strict'; -var ReactFeatureFlags = { +const ReactFeatureFlags = { debugRenderPhaseSideEffects: false, + // TODO (T25573762) Hook this up to a GK for Facaebook engineers (DEV + prod). + debugRenderPhaseSideEffectsForStrictMode: true, + // TODO (T25573607) Enable this warning once deprecation codemod has been run. + warnAboutDeprecatedLifecycles: false, }; module.exports = ReactFeatureFlags; diff --git a/Libraries/Renderer/shims/ReactTypes.js b/Libraries/Renderer/shims/ReactTypes.js index 900582e5d5ac83..0e29678f35b000 100644 --- a/Libraries/Renderer/shims/ReactTypes.js +++ b/Libraries/Renderer/shims/ReactTypes.js @@ -10,11 +10,13 @@ export type ReactNode = | React$Element - | ReactCall - | ReactReturn + | ReactCall + | ReactReturn | ReactPortal | ReactText - | ReactFragment; + | ReactFragment + | ReactProvider + | ReactConsumer; export type ReactFragment = ReactEmpty | Iterable; @@ -24,18 +26,71 @@ export type ReactText = string | number; export type ReactEmpty = null | void | boolean; -export type ReactCall = { +export type ReactCall = { $$typeof: Symbol | number, + type: Symbol | number, key: null | string, - children: any, - // This should be a more specific CallHandler - handler: (props: any, returns: Array) => ReactNodeList, - props: any, + ref: null, + props: { + props: any, + // This should be a more specific CallHandler + handler: (props: any, returns: Array) => ReactNodeList, + children?: ReactNodeList, + }, }; -export type ReactReturn = { +export type ReactReturn = { $$typeof: Symbol | number, - value: mixed, + type: Symbol | number, + key: null, + ref: null, + props: { + value: V, + }, +}; + +export type ReactProvider = { + $$typeof: Symbol | number, + type: ReactProviderType, + key: null | string, + ref: null, + props: { + value: T, + children?: ReactNodeList, + }, +}; + +export type ReactProviderType = { + $$typeof: Symbol | number, + context: ReactContext, +}; + +export type ReactConsumer = { + $$typeof: Symbol | number, + type: ReactContext, + key: null | string, + ref: null, + props: { + render: (value: T) => ReactNodeList, + bits?: number, + }, +}; + +export type ReactContext = { + $$typeof: Symbol | number, + provide(value: T, children: ReactNodeList, key?: string): ReactProvider, + consume( + render: (value: T) => ReactNodeList, + observedBits?: number, + key?: string, + ): ReactConsumer, + calculateChangedBits: ((a: T, b: T) => number) | null, + defaultValue: T, + currentValue: T, + changedBits: number, + + // DEV only + _currentRenderer?: Object | null, }; export type ReactPortal = {