diff --git a/Libraries/Renderer/REVISION b/Libraries/Renderer/REVISION index fe141ab8098..bb434f321ae 100644 --- a/Libraries/Renderer/REVISION +++ b/Libraries/Renderer/REVISION @@ -1 +1 @@ -a634e53d2ff376366aa87815ad4de064494cd05e \ No newline at end of file +ab4280b3e98dbb97d3e753083dab19879167c3f5 \ No newline at end of file diff --git a/Libraries/Renderer/ReactFabric-dev.js b/Libraries/Renderer/ReactFabric-dev.js index 81114b8c8f4..9a30ceeda70 100644 --- a/Libraries/Renderer/ReactFabric-dev.js +++ b/Libraries/Renderer/ReactFabric-dev.js @@ -19,12 +19,11 @@ require("InitializeCore"); var invariant = require("fbjs/lib/invariant"); var warning = require("fbjs/lib/warning"); var emptyFunction = require("fbjs/lib/emptyFunction"); -var RCTEventEmitter = require("RCTEventEmitter"); var UIManager = require("UIManager"); -var React = require("react"); var TextInputState = require("TextInputState"); var deepDiffer = require("deepDiffer"); var flattenStyle = require("flattenStyle"); +var React = require("react"); var emptyObject = require("fbjs/lib/emptyObject"); var checkPropTypes = require("prop-types/checkPropTypes"); var shallowEqual = require("fbjs/lib/shallowEqual"); @@ -557,53 +556,9 @@ var validateEventDispatches = void 0; }; } -/** - * Dispatch the event to the listener. - * @param {SyntheticEvent} event SyntheticEvent to handle - * @param {boolean} simulated If the event is simulated (changes exn behavior) - * @param {function} listener Application-level callback - * @param {*} inst Internal component instance - */ -function executeDispatch(event, simulated, listener, inst) { - var type = event.type || "unknown-event"; - event.currentTarget = getNodeFromInstance(inst); - ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError( - type, - listener, - undefined, - event - ); - event.currentTarget = null; -} - /** * Standard/simple iteration through an event's collected dispatches. */ -function executeDispatchesInOrder(event, simulated) { - var dispatchListeners = event._dispatchListeners; - var dispatchInstances = event._dispatchInstances; - { - validateEventDispatches(event); - } - if (Array.isArray(dispatchListeners)) { - for (var i = 0; i < dispatchListeners.length; i++) { - if (event.isPropagationStopped()) { - break; - } - // Listeners and Instances are two parallel arrays that are always in sync. - executeDispatch( - event, - simulated, - dispatchListeners[i], - dispatchInstances[i] - ); - } - } else if (dispatchListeners) { - executeDispatch(event, simulated, dispatchListeners, dispatchInstances); - } - event._dispatchListeners = null; - event._dispatchInstances = null; -} /** * Standard/simple iteration through an event's collected dispatches, but stops @@ -742,35 +697,6 @@ function forEachAccumulated(arr, cb, scope) { } } -/** - * Internal queue of events that have accumulated their dispatches and are - * waiting to have their dispatches executed. - */ -var eventQueue = null; - -/** - * Dispatches an event and releases it back into the pool, unless persistent. - * - * @param {?object} event Synthetic event to be dispatched. - * @param {boolean} simulated If the event is simulated (changes exn behavior) - * @private - */ -var executeDispatchesAndRelease = function(event, simulated) { - if (event) { - executeDispatchesInOrder(event, simulated); - - if (!event.isPersistent()) { - event.constructor.release(event); - } - } -}; -var executeDispatchesAndReleaseSimulated = function(e) { - return executeDispatchesAndRelease(e, true); -}; -var executeDispatchesAndReleaseTopLevel = function(e) { - return executeDispatchesAndRelease(e, false); -}; - function isInteractive(tag) { return ( tag === "button" || @@ -870,87 +796,6 @@ function getListener(inst, registrationName) { return listener; } -/** - * Allows registered plugins an opportunity to extract events from top-level - * native browser events. - * - * @return {*} An accumulation of synthetic events. - * @internal - */ -function extractEvents( - topLevelType, - targetInst, - nativeEvent, - nativeEventTarget -) { - var events = null; - for (var i = 0; i < plugins.length; i++) { - // Not every plugin in the ordering may be loaded at runtime. - var possiblePlugin = plugins[i]; - if (possiblePlugin) { - var extractedEvents = possiblePlugin.extractEvents( - topLevelType, - targetInst, - nativeEvent, - nativeEventTarget - ); - if (extractedEvents) { - events = accumulateInto(events, extractedEvents); - } - } - } - return events; -} - -function runEventsInBatch(events, simulated) { - if (events !== null) { - eventQueue = accumulateInto(eventQueue, events); - } - - // Set `eventQueue` to null before processing it so that we can tell if more - // events get enqueued while processing. - var processingEventQueue = eventQueue; - eventQueue = null; - - if (!processingEventQueue) { - return; - } - - if (simulated) { - forEachAccumulated( - processingEventQueue, - executeDispatchesAndReleaseSimulated - ); - } else { - forEachAccumulated( - processingEventQueue, - executeDispatchesAndReleaseTopLevel - ); - } - invariant( - !eventQueue, - "processEventQueue(): Additional events were enqueued while processing " + - "an event queue. Support for this has not yet been implemented." - ); - // This would be a good time to rethrow if any of the event handlers threw. - 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; @@ -2323,6 +2168,10 @@ var ReactNativeBridgeEventPlugin = { nativeEvent, nativeEventTarget ) { + if (targetInst == null) { + // Probably a node belonging to another renderer's tree. + return null; + } var bubbleDispatchConfig = customBubblingEventTypes[topLevelType]; var directDispatchConfig = customDirectEventTypes[topLevelType]; invariant( @@ -2434,6 +2283,115 @@ var ReactNativeComponentTree = Object.freeze({ updateFiberProps: updateFiberProps }); +var ReactNativeEventPluginOrder = [ + "ResponderEventPlugin", + "ReactNativeBridgeEventPlugin" +]; + +// Module provided by RN: +var ReactNativeGlobalResponderHandler = { + onChange: function(from, to, blockNativeResponder) { + if (to !== null) { + var tag = to.stateNode._nativeTag; + UIManager.setJSResponder(tag, blockNativeResponder); + } else { + UIManager.clearJSResponder(); + } + } +}; + +/** + * Make sure essential globals are available and are patched correctly. Please don't remove this + * line. Bundles created by react-packager `require` it before executing any application code. This + * ensures it exists in the dependency graph and can be `require`d. + * TODO: require this in packager, not in React #10932517 + */ +// Module provided by RN: +/** + * Inject module for resolving DOM hierarchy and plugin ordering. + */ +injection.injectEventPluginOrder(ReactNativeEventPluginOrder); +injection$1.injectComponentTree(ReactNativeComponentTree); + +ResponderEventPlugin.injection.injectGlobalResponderHandler( + ReactNativeGlobalResponderHandler +); + +/** + * Some important event plugins included by default (without having to require + * them). + */ +injection.injectEventPluginsByName({ + ResponderEventPlugin: ResponderEventPlugin, + ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin +}); + +// TODO: The event emitter registration is interfering with the existing +// ReactNative renderer. So disable it for Fabric for now. + +// import * as ReactNativeEventEmitter from './ReactNativeEventEmitter'; + +// Module provided by RN: +// import RCTEventEmitter from 'RCTEventEmitter'; + +/** + * Register the event emitter with the native bridge + */ +// RCTEventEmitter.register(ReactNativeEventEmitter); + +// The Symbol used to tag the ReactElement-like types. If there is no native Symbol +// nor polyfill, then a plain number is used for performance. +var hasSymbol = typeof Symbol === "function" && Symbol["for"]; + +var REACT_ELEMENT_TYPE = hasSymbol ? Symbol["for"]("react.element") : 0xeac7; +var REACT_CALL_TYPE = hasSymbol ? Symbol["for"]("react.call") : 0xeac8; +var REACT_RETURN_TYPE = hasSymbol ? Symbol["for"]("react.return") : 0xeac9; +var REACT_PORTAL_TYPE = hasSymbol ? Symbol["for"]("react.portal") : 0xeaca; +var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol["for"]("react.fragment") : 0xeacb; +var 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 REACT_ASYNC_MODE_TYPE = hasSymbol + ? Symbol["for"]("react.async_mode") + : 0xeacf; + +var MAYBE_ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator; +var FAUX_ITERATOR_SYMBOL = "@@iterator"; + +function getIteratorFn(maybeIterable) { + if (maybeIterable === null || typeof maybeIterable === "undefined") { + return null; + } + var maybeIterator = + (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || + maybeIterable[FAUX_ITERATOR_SYMBOL]; + if (typeof maybeIterator === "function") { + return maybeIterator; + } + return null; +} + +function createPortal( + children, + containerInfo, + // TODO: figure out the API for cross-renderer implementation. + implementation +) { + var key = + arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; + + return { + // This tag allow us to uniquely identify this as a React Portal + $$typeof: REACT_PORTAL_TYPE, + key: key == null ? null : "" + key, + children: children, + containerInfo: containerInfo, + implementation: implementation + }; +} + // Use to restore controlled state after a change event has fired. var fiberHostComponent = null; @@ -2534,307 +2492,6 @@ var injection$2 = { } }; -/** - * Keeps track of allocating and associating native "tags" which are numeric, - * unique view IDs. All the native tags are negative numbers, to avoid - * collisions, but in the JS we keep track of them as positive integers to store - * them effectively in Arrays. So we must refer to them as "inverses" of the - * native tags (that are * normally negative). - * - * It *must* be the case that every `rootNodeID` always maps to the exact same - * `tag` forever. The easiest way to accomplish this is to never delete - * anything from this table. - * Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to - * unmount a component with a `rootNodeID`, then mount a new one in its place, - */ -var INITIAL_TAG_COUNT = 1; -var ReactNativeTagHandles = { - tagsStartAt: INITIAL_TAG_COUNT, - tagCount: INITIAL_TAG_COUNT, - - allocateTag: function() { - // Skip over root IDs as those are reserved for native - while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) { - ReactNativeTagHandles.tagCount++; - } - var tag = ReactNativeTagHandles.tagCount; - ReactNativeTagHandles.tagCount++; - return tag; - }, - - assertRootTag: function(tag) { - invariant( - this.reactTagIsNativeTopRootID(tag), - "Expect a native root tag, instead got %s", - tag - ); - }, - - reactTagIsNativeTopRootID: function(reactTag) { - // We reserve all tags that are 1 mod 10 for native root views - return reactTag % 10 === 1; - } -}; - -/** - * Version of `ReactBrowserEventEmitter` that works on the receiving side of a - * serialized worker boundary. - */ - -// Shared default empty native event - conserve memory. -var EMPTY_NATIVE_EVENT = {}; - -/** - * Selects a subsequence of `Touch`es, without destroying `touches`. - * - * @param {Array} touches Deserialized touch objects. - * @param {Array} indices Indices by which to pull subsequence. - * @return {Array} Subsequence of touch objects. - */ -var touchSubsequence = function(touches, indices) { - var ret = []; - for (var i = 0; i < indices.length; i++) { - ret.push(touches[indices[i]]); - } - return ret; -}; - -/** - * TODO: Pool all of this. - * - * Destroys `touches` by removing touch objects at indices `indices`. This is - * to maintain compatibility with W3C touch "end" events, where the active - * touches don't include the set that has just been "ended". - * - * @param {Array} touches Deserialized touch objects. - * @param {Array} indices Indices to remove from `touches`. - * @return {Array} Subsequence of removed touch objects. - */ -var removeTouchesAtIndices = function(touches, indices) { - var rippedOut = []; - // use an unsafe downcast to alias to nullable elements, - // so we can delete and then compact. - var temp = touches; - for (var i = 0; i < indices.length; i++) { - var index = indices[i]; - rippedOut.push(touches[index]); - temp[index] = null; - } - var fillAt = 0; - for (var j = 0; j < temp.length; j++) { - var cur = temp[j]; - if (cur !== null) { - temp[fillAt++] = cur; - } - } - temp.length = fillAt; - return rippedOut; -}; - -/** - * Internal version of `receiveEvent` in terms of normalized (non-tag) - * `rootNodeID`. - * - * @see receiveEvent. - * - * @param {rootNodeID} rootNodeID React root node ID that event occurred on. - * @param {TopLevelType} topLevelType Top level type of event. - * @param {?object} nativeEventParam Object passed from native. - */ -function _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam) { - var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT; - var inst = getInstanceFromTag(rootNodeID); - batchedUpdates(function() { - runExtractedEventsInBatch( - topLevelType, - inst, - nativeEvent, - nativeEvent.target - ); - }); - // React Native doesn't use ReactControlledComponent but if it did, here's - // where it would do it. -} - -/** - * Publicly exposed method on module for native objc to invoke when a top - * level event is extracted. - * @param {rootNodeID} rootNodeID React root node ID that event occurred on. - * @param {TopLevelType} topLevelType Top level type of event. - * @param {object} nativeEventParam Object passed from native. - */ -function receiveEvent(rootNodeID, topLevelType, nativeEventParam) { - _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam); -} - -/** - * Simple multi-wrapper around `receiveEvent` that is intended to receive an - * efficient representation of `Touch` objects, and other information that - * can be used to construct W3C compliant `Event` and `Touch` lists. - * - * This may create dispatch behavior that differs than web touch handling. We - * loop through each of the changed touches and receive it as a single event. - * So two `touchStart`/`touchMove`s that occur simultaneously are received as - * two separate touch event dispatches - when they arguably should be one. - * - * This implementation reuses the `Touch` objects themselves as the `Event`s - * since we dispatch an event for each touch (though that might not be spec - * compliant). The main purpose of reusing them is to save allocations. - * - * TODO: Dispatch multiple changed touches in one event. The bubble path - * could be the first common ancestor of all the `changedTouches`. - * - * One difference between this behavior and W3C spec: cancelled touches will - * not appear in `.touches`, or in any future `.touches`, though they may - * still be "actively touching the surface". - * - * Web desktop polyfills only need to construct a fake touch event with - * identifier 0, also abandoning traditional click handlers. - */ -function receiveTouches(eventTopLevelType, touches, changedIndices) { - var changedTouches = - eventTopLevelType === "topTouchEnd" || - eventTopLevelType === "topTouchCancel" - ? removeTouchesAtIndices(touches, changedIndices) - : touchSubsequence(touches, changedIndices); - - for (var jj = 0; jj < changedTouches.length; jj++) { - var touch = changedTouches[jj]; - // Touch objects can fulfill the role of `DOM` `Event` objects if we set - // the `changedTouches`/`touches`. This saves allocations. - touch.changedTouches = changedTouches; - touch.touches = touches; - var nativeEvent = touch; - var rootNodeID = null; - var target = nativeEvent.target; - if (target !== null && target !== undefined) { - if (target < ReactNativeTagHandles.tagsStartAt) { - { - warning( - false, - "A view is reporting that a touch occurred on tag zero." - ); - } - } else { - rootNodeID = target; - } - } - // $FlowFixMe Shouldn't we *not* call it if rootNodeID is null? - _receiveRootNodeIDEvent(rootNodeID, eventTopLevelType, nativeEvent); - } -} - -var ReactNativeEventEmitter = Object.freeze({ - getListener: getListener, - registrationNames: registrationNameModules, - _receiveRootNodeIDEvent: _receiveRootNodeIDEvent, - receiveEvent: receiveEvent, - receiveTouches: receiveTouches -}); - -var ReactNativeEventPluginOrder = [ - "ResponderEventPlugin", - "ReactNativeBridgeEventPlugin" -]; - -// Module provided by RN: -var ReactNativeGlobalResponderHandler = { - onChange: function(from, to, blockNativeResponder) { - if (to !== null) { - var tag = to.stateNode._nativeTag; - UIManager.setJSResponder(tag, blockNativeResponder); - } else { - UIManager.clearJSResponder(); - } - } -}; - -/** - * Make sure essential globals are available and are patched correctly. Please don't remove this - * line. Bundles created by react-packager `require` it before executing any application code. This - * ensures it exists in the dependency graph and can be `require`d. - * TODO: require this in packager, not in React #10932517 - */ -// Module provided by RN: -// Module provided by RN: -/** - * Register the event emitter with the native bridge - */ -// TODO: This event emitter is interfering with the existing ReactNative renderer. -// RCTEventEmitter.register(ReactNativeEventEmitter); - -/** - * Inject module for resolving DOM hierarchy and plugin ordering. - */ -injection.injectEventPluginOrder(ReactNativeEventPluginOrder); -injection$1.injectComponentTree(ReactNativeComponentTree); - -ResponderEventPlugin.injection.injectGlobalResponderHandler( - ReactNativeGlobalResponderHandler -); - -/** - * Some important event plugins included by default (without having to require - * them). - */ -injection.injectEventPluginsByName({ - ResponderEventPlugin: ResponderEventPlugin, - ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin -}); - -// The Symbol used to tag the ReactElement-like types. If there is no native Symbol -// nor polyfill, then a plain number is used for performance. -var hasSymbol = typeof Symbol === "function" && Symbol["for"]; - -var REACT_ELEMENT_TYPE = hasSymbol ? Symbol["for"]("react.element") : 0xeac7; -var REACT_CALL_TYPE = hasSymbol ? Symbol["for"]("react.call") : 0xeac8; -var REACT_RETURN_TYPE = hasSymbol ? Symbol["for"]("react.return") : 0xeac9; -var REACT_PORTAL_TYPE = hasSymbol ? Symbol["for"]("react.portal") : 0xeaca; -var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol["for"]("react.fragment") : 0xeacb; -var 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 REACT_ASYNC_MODE_TYPE = hasSymbol - ? Symbol["for"]("react.async_mode") - : 0xeacf; - -var MAYBE_ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator; -var FAUX_ITERATOR_SYMBOL = "@@iterator"; - -function getIteratorFn(maybeIterable) { - if (maybeIterable === null || typeof maybeIterable === "undefined") { - return null; - } - var maybeIterator = - (MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) || - maybeIterable[FAUX_ITERATOR_SYMBOL]; - if (typeof maybeIterator === "function") { - return maybeIterator; - } - return null; -} - -function createPortal( - children, - containerInfo, - // TODO: figure out the API for cross-renderer implementation. - implementation -) { - var key = - arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; - - return { - // This tag allow us to uniquely identify this as a React Portal - $$typeof: REACT_PORTAL_TYPE, - key: key == null ? null : "" + key, - children: children, - containerInfo: containerInfo, - implementation: implementation - }; -} - var TouchHistoryMath = { /** * This code is optimized and not intended to look beautiful. This allows @@ -2978,19 +2635,9 @@ var TouchHistoryMath = { noCentroid: -1 }; -var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - -var ReactCurrentOwner = ReactInternals.ReactCurrentOwner; -var ReactDebugCurrentFrame = ReactInternals.ReactDebugCurrentFrame; - -var ReactGlobalSharedState = Object.freeze({ - ReactCurrentOwner: ReactCurrentOwner, - ReactDebugCurrentFrame: ReactDebugCurrentFrame -}); - // TODO: this is special because it gets imported during build. -var ReactVersion = "16.3.0-alpha.0"; +var ReactVersion = "16.3.0-alpha.1"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { @@ -3581,6 +3228,11 @@ function set(key, value) { key._reactInternalFiber = value; } +var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + +var ReactCurrentOwner = ReactInternals.ReactCurrentOwner; +var ReactDebugCurrentFrame = ReactInternals.ReactDebugCurrentFrame; + function getComponentName(fiber) { var type = fiber.type; @@ -4190,19 +3842,26 @@ function get$1(name) { return viewConfig; } -// Don't change these two values: -var NoEffect = 0; -var PerformedWork = 1; +// Don't change these two values. They're used by React Dev Tools. +var NoEffect = /* */ 0; +var PerformedWork = /* */ 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 Placement = /* */ 2; +var Update = /* */ 4; +var PlacementAndUpdate = /* */ 6; +var Deletion = /* */ 8; +var ContentReset = /* */ 16; +var Callback = /* */ 32; +var DidCapture = /* */ 64; +var Ref = /* */ 128; +var ErrLog = /* */ 256; + +// Union of all host effects +var HostEffectMask = /* */ 511; + +var Incomplete = /* */ 512; +var ShouldCapture = /* */ 1024; var MOUNTING = 1; var MOUNTED = 2; @@ -4635,7 +4294,9 @@ var debugRenderPhaseSideEffects = false; var debugRenderPhaseSideEffectsForStrictMode = false; var enableUserTimingAPI = true; +var enableGetDerivedStateFromCatch = false; var warnAboutDeprecatedLifecycles = false; +var replayFailedUnitOfWorkWithInvokeGuardedCallback = true; // React Fabric uses persistent reconciler. var enableMutatingReconciler = false; @@ -4772,6 +4433,8 @@ var shouldIgnoreFiber = function(fiber) { case CallComponent: case ReturnComponent: case Fragment: + case ContextProvider: + case ContextConsumer: return true; default: return false; @@ -5681,12 +5344,12 @@ function createFiberRoot(containerInfo, isAsync, hydrate) { current: uninitializedFiber, containerInfo: containerInfo, pendingChildren: null, - remainingExpirationTime: NoWork, - isReadyForCommit: false, + pendingCommitExpirationTime: NoWork, finishedWork: null, context: null, pendingContext: null, hydrate: hydrate, + remainingExpirationTime: NoWork, firstBatch: null, nextScheduledRoot: null }; @@ -6128,7 +5791,8 @@ function createUpdateQueue(baseState) { last: null, callbackList: null, hasForceUpdate: false, - isInitialized: false + isInitialized: false, + capturedValues: null }; { queue.isProcessing = false; @@ -6153,7 +5817,10 @@ function insertUpdateIntoQueue(queue, update) { } } -function insertUpdateIntoFiber(fiber, update) { +var q1 = void 0; +var q2 = void 0; +function ensureUpdateQueues(fiber) { + q1 = q2 = null; // We'll have at least one and at most two distinct update queues. var alternateFiber = fiber.alternate; var queue1 = fiber.updateQueue; @@ -6176,6 +5843,16 @@ function insertUpdateIntoFiber(fiber, update) { } queue2 = queue2 !== queue1 ? queue2 : null; + // Use module variables instead of returning a tuple + q1 = queue1; + q2 = queue2; +} + +function insertUpdateIntoFiber(fiber, update) { + ensureUpdateQueues(fiber); + var queue1 = q1; + var queue2 = q2; + // Warn if an update is scheduled from inside an updater function. { if ( @@ -6215,14 +5892,17 @@ function insertUpdateIntoFiber(fiber, update) { } function getUpdateExpirationTime(fiber) { - if (fiber.tag !== ClassComponent && fiber.tag !== HostRoot) { - return NoWork; + switch (fiber.tag) { + case HostRoot: + case ClassComponent: + var updateQueue = fiber.updateQueue; + if (updateQueue === null) { + return NoWork; + } + return updateQueue.expirationTime; + default: + return NoWork; } - var updateQueue = fiber.updateQueue; - if (updateQueue === null) { - return NoWork; - } - return updateQueue.expirationTime; } function getStateFromUpdate(update, instance, prevState, props) { @@ -6251,6 +5931,7 @@ function processUpdateQueue( first: currentQueue.first, last: currentQueue.last, isInitialized: currentQueue.isInitialized, + capturedValues: currentQueue.capturedValues, // These fields are no longer valid because they were already committed. // Reset them. callbackList: null, @@ -6352,12 +6033,24 @@ function processUpdateQueue( } _callbackList.push(update); } + if (update.capturedValue !== null) { + var _capturedValues = queue.capturedValues; + if (_capturedValues === null) { + queue.capturedValues = [update.capturedValue]; + } else { + _capturedValues.push(update.capturedValue); + } + } update = update.next; } if (queue.callbackList !== null) { workInProgress.effectTag |= Callback; - } else if (queue.first === null && !queue.hasForceUpdate) { + } else if ( + queue.first === null && + !queue.hasForceUpdate && + queue.capturedValues === null + ) { // The queue is empty. We can reset it. workInProgress.updateQueue = null; } @@ -6453,6 +6146,18 @@ var warnOnInvalidCallback = void 0; }); Object.freeze(fakeInternalInstance); } +function callGetDerivedStateFromCatch(ctor, capturedValues) { + var resultState = {}; + for (var i = 0; i < capturedValues.length; i++) { + var capturedValue = capturedValues[i]; + var error = capturedValue.value; + var partialState = ctor.getDerivedStateFromCatch.call(null, error); + if (partialState !== null && partialState !== undefined) { + Object.assign(resultState, partialState); + } + } + return resultState; +} var ReactFiberClassComponent = function( scheduleWork, @@ -6476,7 +6181,7 @@ var ReactFiberClassComponent = function( callback: callback, isReplace: false, isForced: false, - nextCallback: null, + capturedValue: null, next: null }; insertUpdateIntoFiber(fiber, update); @@ -6495,7 +6200,7 @@ var ReactFiberClassComponent = function( callback: callback, isReplace: true, isForced: false, - nextCallback: null, + capturedValue: null, next: null }; insertUpdateIntoFiber(fiber, update); @@ -6514,7 +6219,7 @@ var ReactFiberClassComponent = function( callback: callback, isReplace: false, isForced: true, - nextCallback: null, + capturedValue: null, next: null }; insertUpdateIntoFiber(fiber, update); @@ -6540,7 +6245,7 @@ var ReactFiberClassComponent = function( } var instance = workInProgress.stateNode; - var type = workInProgress.type; + var ctor = workInProgress.type; if (typeof instance.shouldComponentUpdate === "function") { startPhaseTimer(workInProgress, "shouldComponentUpdate"); var shouldUpdate = instance.shouldComponentUpdate( @@ -6562,7 +6267,7 @@ var ReactFiberClassComponent = function( return shouldUpdate; } - if (type.prototype && type.prototype.isPureReactComponent) { + if (ctor.prototype && ctor.prototype.isPureReactComponent) { return ( !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) ); @@ -6718,7 +6423,7 @@ var ReactFiberClassComponent = function( } if (typeof instance.getChildContext === "function") { warning( - typeof workInProgress.type.childContextTypes === "object", + typeof type.childContextTypes === "object", "%s.getChildContext(): childContextTypes must be defined in order to " + "use getChildContext().", getComponentName(workInProgress) @@ -6939,6 +6644,7 @@ var ReactFiberClassComponent = function( // Invokes the mount life-cycles on a previously never rendered instance. function mountClassInstance(workInProgress, renderExpirationTime) { + var ctor = workInProgress.type; var current = workInProgress.alternate; { @@ -6975,7 +6681,7 @@ var ReactFiberClassComponent = function( if ( (typeof instance.UNSAFE_componentWillMount === "function" || typeof instance.componentWillMount === "function") && - typeof workInProgress.type.getDerivedStateFromProps !== "function" + typeof ctor.getDerivedStateFromProps !== "function" ) { callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's @@ -6997,113 +6703,8 @@ var ReactFiberClassComponent = function( } } - // Called on a preexisting class instance. Returns false if a resumed render - // could be reused. - // function resumeMountClassInstance( - // workInProgress: Fiber, - // priorityLevel: PriorityLevel, - // ): boolean { - // const instance = workInProgress.stateNode; - // resetInputPointers(workInProgress, instance); - - // let newState = workInProgress.memoizedState; - // let newProps = workInProgress.pendingProps; - // if (!newProps) { - // // If there isn't any new props, then we'll reuse the memoized props. - // // This could be from already completed work. - // newProps = workInProgress.memoizedProps; - // invariant( - // newProps != null, - // 'There should always be pending or memoized props. This error is ' + - // 'likely caused by a bug in React. Please file an issue.', - // ); - // } - // const newUnmaskedContext = getUnmaskedContext(workInProgress); - // const newContext = getMaskedContext(workInProgress, newUnmaskedContext); - - // const oldContext = instance.context; - // const oldProps = workInProgress.memoizedProps; - - // if ( - // typeof instance.componentWillReceiveProps === 'function' && - // (oldProps !== newProps || oldContext !== newContext) - // ) { - // callComponentWillReceiveProps( - // workInProgress, - // instance, - // newProps, - // newContext, - // ); - // } - - // // Process the update queue before calling shouldComponentUpdate - // const updateQueue = workInProgress.updateQueue; - // if (updateQueue !== null) { - // newState = processUpdateQueue( - // workInProgress, - // updateQueue, - // instance, - // newState, - // newProps, - // priorityLevel, - // ); - // } - - // // TODO: Should we deal with a setState that happened after the last - // // componentWillMount and before this componentWillMount? Probably - // // unsupported anyway. - - // if ( - // !checkShouldComponentUpdate( - // workInProgress, - // workInProgress.memoizedProps, - // newProps, - // workInProgress.memoizedState, - // newState, - // newContext, - // ) - // ) { - // // Update the existing instance's state, props, and context pointers even - // // though we're bailing out. - // instance.props = newProps; - // instance.state = newState; - // instance.context = newContext; - // return false; - // } - - // // Update the input pointers now so that they are correct when we call - // // componentWillMount - // instance.props = newProps; - // instance.state = newState; - // instance.context = newContext; - - // if (typeof instance.componentWillMount === 'function') { - // callComponentWillMount(workInProgress, instance); - // // componentWillMount may have called setState. Process the update queue. - // const newUpdateQueue = workInProgress.updateQueue; - // if (newUpdateQueue !== null) { - // newState = processUpdateQueue( - // workInProgress, - // newUpdateQueue, - // instance, - // newState, - // newProps, - // priorityLevel, - // ); - // } - // } - - // if (typeof instance.componentDidMount === 'function') { - // workInProgress.effectTag |= Update; - // } - - // instance.state = newState; - - // return true; - // } - - // Invokes the update life-cycles and returns false if it shouldn't rerender. - function updateClassInstance(current, workInProgress, renderExpirationTime) { + function resumeMountClassInstance(workInProgress, renderExpirationTime) { + var ctor = workInProgress.type; var instance = workInProgress.stateNode; resetInputPointers(workInProgress, instance); @@ -7122,7 +6723,7 @@ var ReactFiberClassComponent = function( if ( (typeof instance.UNSAFE_componentWillReceiveProps === "function" || typeof instance.componentWillReceiveProps === "function") && - typeof workInProgress.type.getDerivedStateFromProps !== "function" + typeof ctor.getDerivedStateFromProps !== "function" ) { if (oldProps !== newProps || oldContext !== newContext) { callComponentWillReceiveProps( @@ -7134,9 +6735,9 @@ var ReactFiberClassComponent = function( } } - var partialState = void 0; + var derivedStateFromProps = void 0; if (oldProps !== newProps) { - partialState = callGetDerivedStateFromProps( + derivedStateFromProps = callGetDerivedStateFromProps( workInProgress, instance, newProps @@ -7147,6 +6748,172 @@ var ReactFiberClassComponent = function( var oldState = workInProgress.memoizedState; // TODO: Previous state can be null. var newState = void 0; + var derivedStateFromCatch = void 0; + if (workInProgress.updateQueue !== null) { + newState = processUpdateQueue( + null, + workInProgress, + workInProgress.updateQueue, + instance, + newProps, + renderExpirationTime + ); + + var updateQueue = workInProgress.updateQueue; + if ( + updateQueue !== null && + updateQueue.capturedValues !== null && + enableGetDerivedStateFromCatch && + typeof ctor.getDerivedStateFromCatch === "function" + ) { + var capturedValues = updateQueue.capturedValues; + // Don't remove these from the update queue yet. We need them in + // finishClassComponent. Do the reset there. + // TODO: This is awkward. Refactor class components. + // updateQueue.capturedValues = null; + derivedStateFromCatch = callGetDerivedStateFromCatch( + ctor, + capturedValues + ); + } + } else { + newState = oldState; + } + + if (derivedStateFromProps !== null && derivedStateFromProps !== 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 + ? derivedStateFromProps + : Object.assign({}, newState, derivedStateFromProps); + } + if (derivedStateFromCatch !== null && derivedStateFromCatch !== 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 + ? derivedStateFromCatch + : Object.assign({}, newState, derivedStateFromCatch); + } + + 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.componentDidMount === "function") { + workInProgress.effectTag |= Update; + } + return false; + } + + var shouldUpdate = checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldState, + newState, + newContext + ); + + 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 ctor.getDerivedStateFromProps !== "function" + ) { + startPhaseTimer(workInProgress, "componentWillUpdate"); + if (typeof instance.componentWillUpdate === "function") { + instance.componentWillUpdate(newProps, newState, newContext); + } + if (typeof instance.UNSAFE_componentWillUpdate === "function") { + 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.componentDidMount === "function") { + workInProgress.effectTag |= Update; + } + + // If shouldComponentUpdate returned false, we should still update the + // memoized props/state to indicate that this work can be reused. + memoizeProps(workInProgress, newProps); + memoizeState(workInProgress, newState); + } + + // Update the existing instance's state, props, and context pointers even + // if shouldComponentUpdate returns false. + instance.props = newProps; + instance.state = newState; + instance.context = newContext; + + return shouldUpdate; + } + + // Invokes the update life-cycles and returns false if it shouldn't rerender. + function updateClassInstance(current, workInProgress, renderExpirationTime) { + var ctor = workInProgress.type; + var instance = workInProgress.stateNode; + resetInputPointers(workInProgress, instance); + + var oldProps = workInProgress.memoizedProps; + var newProps = workInProgress.pendingProps; + var oldContext = instance.context; + var newUnmaskedContext = getUnmaskedContext(workInProgress); + var newContext = getMaskedContext(workInProgress, newUnmaskedContext); + + // Note: During these life-cycles, instance.props/instance.state are what + // ever the previously attempted to render - not the "current". However, + // during componentDidUpdate we pass the "current" props. + + // 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 ctor.getDerivedStateFromProps !== "function" + ) { + if (oldProps !== newProps || oldContext !== newContext) { + callComponentWillReceiveProps( + workInProgress, + instance, + newProps, + newContext + ); + } + } + + var derivedStateFromProps = void 0; + if (oldProps !== newProps) { + derivedStateFromProps = callGetDerivedStateFromProps( + workInProgress, + instance, + newProps + ); + } + + // 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; + var derivedStateFromCatch = void 0; if (workInProgress.updateQueue !== null) { newState = processUpdateQueue( current, @@ -7156,18 +6923,45 @@ var ReactFiberClassComponent = function( newProps, renderExpirationTime ); + + var updateQueue = workInProgress.updateQueue; + if ( + updateQueue !== null && + updateQueue.capturedValues !== null && + enableGetDerivedStateFromCatch && + typeof ctor.getDerivedStateFromCatch === "function" + ) { + var capturedValues = updateQueue.capturedValues; + // Don't remove these from the update queue yet. We need them in + // finishClassComponent. Do the reset there. + // TODO: This is awkward. Refactor class components. + // updateQueue.capturedValues = null; + derivedStateFromCatch = callGetDerivedStateFromCatch( + ctor, + capturedValues + ); + } } else { newState = oldState; } - if (partialState !== null && partialState !== undefined) { + if (derivedStateFromProps !== null && derivedStateFromProps !== 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); + ? derivedStateFromProps + : Object.assign({}, newState, derivedStateFromProps); + } + if (derivedStateFromCatch !== null && derivedStateFromCatch !== 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 + ? derivedStateFromCatch + : Object.assign({}, newState, derivedStateFromCatch); } if ( @@ -7207,7 +7001,7 @@ var ReactFiberClassComponent = function( if ( (typeof instance.UNSAFE_componentWillUpdate === "function" || typeof instance.componentWillUpdate === "function") && - typeof workInProgress.type.getDerivedStateFromProps !== "function" + typeof ctor.getDerivedStateFromProps !== "function" ) { startPhaseTimer(workInProgress, "componentWillUpdate"); if (typeof instance.componentWillUpdate === "function") { @@ -7253,7 +7047,7 @@ var ReactFiberClassComponent = function( callGetDerivedStateFromProps: callGetDerivedStateFromProps, constructClassInstance: constructClassInstance, mountClassInstance: mountClassInstance, - // resumeMountClassInstance, + resumeMountClassInstance: resumeMountClassInstance, updateClassInstance: updateClassInstance }; }; @@ -8621,6 +8415,7 @@ var ReactFiberBeginWork = function( _ReactFiberClassCompo.callGetDerivedStateFromProps, constructClassInstance = _ReactFiberClassCompo.constructClassInstance, mountClassInstance = _ReactFiberClassCompo.mountClassInstance, + resumeMountClassInstance = _ReactFiberClassCompo.resumeMountClassInstance, updateClassInstance = _ReactFiberClassCompo.updateClassInstance; // TODO: Remove this and use reconcileChildrenAtExpirationTime directly. @@ -8745,19 +8540,20 @@ var ReactFiberBeginWork = function( // 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) { + if (workInProgress.stateNode === null) { // 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); + shouldUpdate = resumeMountClassInstance( + workInProgress, + renderExpirationTime + ); } } else { shouldUpdate = updateClassInstance( @@ -8766,11 +8562,23 @@ var ReactFiberBeginWork = function( renderExpirationTime ); } + + // We processed the update queue inside updateClassInstance. It may have + // included some errors that were dispatched during the commit phase. + // TODO: Refactor class components so this is less awkward. + var didCaptureError = false; + var updateQueue = workInProgress.updateQueue; + if (updateQueue !== null && updateQueue.capturedValues !== null) { + shouldUpdate = true; + didCaptureError = true; + } return finishClassComponent( current, workInProgress, shouldUpdate, - hasContext + hasContext, + didCaptureError, + renderExpirationTime ); } @@ -8778,12 +8586,14 @@ var ReactFiberBeginWork = function( current, workInProgress, shouldUpdate, - hasContext + hasContext, + didCaptureError, + renderExpirationTime ) { // Refs should update even if shouldComponentUpdate returns false markRef(current, workInProgress); - if (!shouldUpdate) { + if (!shouldUpdate && !didCaptureError) { // Context providers should defer to sCU for rendering if (hasContext) { invalidateContextProvider(workInProgress, false); @@ -8792,26 +8602,60 @@ var ReactFiberBeginWork = function( return bailoutOnAlreadyFinishedWork(current, workInProgress); } + var ctor = workInProgress.type; var instance = workInProgress.stateNode; // Rerender ReactCurrentOwner.current = workInProgress; var nextChildren = void 0; - { - ReactDebugCurrentFiber.setCurrentPhase("render"); - nextChildren = instance.render(); - if ( - debugRenderPhaseSideEffects || - (debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode) - ) { - instance.render(); + if ( + didCaptureError && + (!enableGetDerivedStateFromCatch || + typeof ctor.getDerivedStateFromCatch !== "function") + ) { + // If we captured an error, but getDerivedStateFrom catch is not defined, + // unmount all the children. componentDidCatch will schedule an update to + // re-render a fallback. This is temporary until we migrate everyone to + // the new API. + // TODO: Warn in a future release. + nextChildren = null; + } else { + { + ReactDebugCurrentFiber.setCurrentPhase("render"); + nextChildren = instance.render(); + if ( + debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.mode & StrictMode) + ) { + instance.render(); + } + ReactDebugCurrentFiber.setCurrentPhase(null); } - ReactDebugCurrentFiber.setCurrentPhase(null); } + // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; - reconcileChildren(current, workInProgress, nextChildren); + if (didCaptureError) { + // If we're recovering from an error, reconcile twice: first to delete + // all the existing children. + reconcileChildrenAtExpirationTime( + current, + workInProgress, + null, + renderExpirationTime + ); + workInProgress.child = null; + // Now we can continue reconciling like normal. This has the effect of + // remounting all children regardless of whether their their + // identity matches. + } + reconcileChildrenAtExpirationTime( + current, + workInProgress, + nextChildren, + renderExpirationTime + ); // 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); @@ -8853,13 +8697,21 @@ var ReactFiberBeginWork = function( null, renderExpirationTime ); - if (prevState === state) { + memoizeState(workInProgress, state); + updateQueue = workInProgress.updateQueue; + + var element = void 0; + if (updateQueue !== null && updateQueue.capturedValues !== null) { + // There's an uncaught error. Unmount the whole root. + element = null; + } else 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); + } else { + element = state.element; } - var element = state.element; var root = workInProgress.stateNode; if ( (current === null || current.child === null) && @@ -8916,7 +8768,18 @@ var ReactFiberBeginWork = function( // 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 isHidden = + workInProgress.mode & AsyncMode && + shouldDeprioritizeSubtree(type, nextProps); + if (isHidden) { + // Before bailing out, make sure we've deprioritized a hidden component. + workInProgress.expirationTime = Never; + } + if (!isHidden || renderExpirationTime !== Never) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + // If we're rendering a hidden node at hidden priority, don't bailout. The + // parent is complete, but the children may not be. } var nextChildren = nextProps.children; @@ -8945,6 +8808,7 @@ var ReactFiberBeginWork = function( // Down-prioritize the children. workInProgress.expirationTime = Never; // Bailout and come back to this fiber later. + workInProgress.memoizedProps = nextProps; return null; } @@ -9038,7 +8902,14 @@ var ReactFiberBeginWork = function( var hasContext = pushContextProvider(workInProgress); adoptClassInstance(workInProgress, value); mountClassInstance(workInProgress, renderExpirationTime); - return finishClassComponent(current, workInProgress, true, hasContext); + return finishClassComponent( + current, + workInProgress, + true, + hasContext, + false, + renderExpirationTime + ); } else { // Proceed under the assumption that this is a functional component workInProgress.tag = FunctionalComponent; @@ -9366,6 +9237,16 @@ var ReactFiberBeginWork = function( workInProgress.stateNode = observedBits; var render = newProps.children; + + if (typeof render !== "function") { + invariant( + false, + "A context consumer was rendered with multiple children, or a child that isn't a function. " + + "A context consumer expects a single child that is a function. " + + "If you did pass a function, make sure there is no trailing or leading whitespace around it." + ); + } + var newChildren = render(newValue); reconcileChildren(current, workInProgress, newChildren); return workInProgress.child; @@ -9527,70 +9408,8 @@ var ReactFiberBeginWork = function( } } - function beginFailedWork(current, workInProgress, renderExpirationTime) { - // Push context providers here to avoid a push/pop context mismatch. - switch (workInProgress.tag) { - case ClassComponent: - pushContextProvider(workInProgress); - break; - case HostRoot: - pushHostRootContext(workInProgress); - break; - default: - invariant( - false, - "Invalid type of work. This error is likely caused by a bug in React. " + - "Please file an issue." - ); - } - - // Add an error effect so we can handle the error during the commit phase - workInProgress.effectTag |= Err; - - // This is a weird case where we do "resume" work — work that failed on - // our first attempt. Because we no longer have a notion of "progressed - // deletions," reset the child to the current child to make sure we delete - // it again. TODO: Find a better way to handle this, perhaps during a more - // general overhaul of error handling. - if (current === null) { - workInProgress.child = null; - } else if (workInProgress.child !== current.child) { - workInProgress.child = current.child; - } - - if ( - workInProgress.expirationTime === NoWork || - workInProgress.expirationTime > renderExpirationTime - ) { - return bailoutOnLowPriority(current, workInProgress); - } - - // If we don't bail out, we're going be recomputing our children so we need - // to drop our effect list. - workInProgress.firstEffect = null; - workInProgress.lastEffect = null; - - // Unmount the current children as if the component rendered null - var nextChildren = null; - reconcileChildrenAtExpirationTime( - current, - workInProgress, - nextChildren, - renderExpirationTime - ); - - if (workInProgress.tag === ClassComponent) { - var instance = workInProgress.stateNode; - workInProgress.memoizedProps = instance.props; - workInProgress.memoizedState = instance.state; - } - - return workInProgress.child; - } - return { - beginWork: beginWork, - beginFailedWork: beginFailedWork + beginWork: beginWork }; }; @@ -9808,14 +9627,12 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { } 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); + portalOrRoot.pendingChildren = newChildSet; // Schedule an update on the container to swap out the container. markUpdate(workInProgress); + finalizeContainerChildren(container, newChildSet); } }; updateHostComponent = function( @@ -9924,6 +9741,20 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { case ClassComponent: { // We are leaving this subtree, so pop context if any. popContextProvider(workInProgress); + + // If this component caught an error, schedule an error log effect. + var instance = workInProgress.stateNode; + var updateQueue = workInProgress.updateQueue; + if (updateQueue !== null && updateQueue.capturedValues !== null) { + workInProgress.effectTag &= ~DidCapture; + if (typeof instance.componentDidCatch === "function") { + workInProgress.effectTag |= ErrLog; + } else { + // Normally we clear this in the commit phase, but since we did not + // schedule an effect, we need to reset it here. + updateQueue.capturedValues = null; + } + } return null; } case HostRoot: { @@ -9934,7 +9765,6 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { fiberRoot.context = fiberRoot.pendingContext; fiberRoot.pendingContext = null; } - if (current === null || current.child === null) { // If we hydrated, pop so that we can delete any remaining children // that weren't hydrated. @@ -9944,6 +9774,11 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { workInProgress.effectTag &= ~Placement; } updateHostContainer(workInProgress); + + var _updateQueue = workInProgress.updateQueue; + if (_updateQueue !== null && _updateQueue.capturedValues !== null) { + workInProgress.effectTag |= ErrLog; + } return null; } case HostComponent: { @@ -9958,10 +9793,13 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { // 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 _instance = workInProgress.stateNode; var currentHostContext = getHostContext(); + // TODO: Experiencing an error where oldProps is null. Suggests a host + // component is hitting the resume path. Figure out why. Possibly + // related to `hidden`. var updatePayload = prepareUpdate( - instance, + _instance, type, oldProps, newProps, @@ -10015,7 +9853,7 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { markUpdate(workInProgress); } } else { - var _instance = createInstance( + var _instance2 = createInstance( type, newProps, rootContainerInstance, @@ -10023,14 +9861,14 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { workInProgress ); - appendAllChildren(_instance, workInProgress); + appendAllChildren(_instance2, 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, + _instance2, type, newProps, rootContainerInstance, @@ -10039,7 +9877,7 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { ) { markUpdate(workInProgress); } - workInProgress.stateNode = _instance; + workInProgress.stateNode = _instance2; } if (workInProgress.ref !== null) { @@ -10134,11 +9972,233 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { }; }; +function createCapturedValue(value, source) { + // If the value is an error, call this function immediately after it is thrown + // so the stack is accurate. + return { + value: value, + source: source, + stack: getStackAddendumByWorkInProgressFiber(source) + }; +} + +var ReactFiberUnwindWork = function( + hostContext, + scheduleWork, + isAlreadyFailedLegacyErrorBoundary +) { + var popHostContainer = hostContext.popHostContainer, + popHostContext = hostContext.popHostContext; + + function throwException(returnFiber, sourceFiber, rawValue) { + // The source fiber did not complete. + sourceFiber.effectTag |= Incomplete; + // Its effect list is no longer valid. + sourceFiber.firstEffect = sourceFiber.lastEffect = null; + + var value = createCapturedValue(rawValue, sourceFiber); + + var workInProgress = returnFiber; + do { + switch (workInProgress.tag) { + case HostRoot: { + // Uncaught error + var errorInfo = value; + ensureUpdateQueues(workInProgress); + var updateQueue = workInProgress.updateQueue; + updateQueue.capturedValues = [errorInfo]; + workInProgress.effectTag |= ShouldCapture; + return; + } + case ClassComponent: + // Capture and retry + var ctor = workInProgress.type; + var _instance = workInProgress.stateNode; + if ( + (workInProgress.effectTag & DidCapture) === NoEffect && + ((typeof ctor.getDerivedStateFromCatch === "function" && + enableGetDerivedStateFromCatch) || + (_instance !== null && + typeof _instance.componentDidCatch === "function" && + !isAlreadyFailedLegacyErrorBoundary(_instance))) + ) { + ensureUpdateQueues(workInProgress); + var _updateQueue = workInProgress.updateQueue; + var capturedValues = _updateQueue.capturedValues; + if (capturedValues === null) { + _updateQueue.capturedValues = [value]; + } else { + capturedValues.push(value); + } + workInProgress.effectTag |= ShouldCapture; + return; + } + break; + default: + break; + } + workInProgress = workInProgress["return"]; + } while (workInProgress !== null); + } + + function unwindWork(workInProgress) { + switch (workInProgress.tag) { + case ClassComponent: { + popContextProvider(workInProgress); + var effectTag = workInProgress.effectTag; + if (effectTag & ShouldCapture) { + workInProgress.effectTag = (effectTag & ~ShouldCapture) | DidCapture; + return workInProgress; + } + return null; + } + case HostRoot: { + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + var _effectTag = workInProgress.effectTag; + if (_effectTag & ShouldCapture) { + workInProgress.effectTag = (_effectTag & ~ShouldCapture) | DidCapture; + return workInProgress; + } + return null; + } + case HostComponent: { + popHostContext(workInProgress); + return null; + } + case HostPortal: + popHostContainer(workInProgress); + return null; + case ContextProvider: + popProvider(workInProgress); + return null; + default: + return null; + } + } + return { + throwException: throwException, + unwindWork: unwindWork + }; +}; + +// This module is forked in different environments. +// By default, return `true` to log errors to the console. +// Forks can return `false` if this isn't desirable. +function showErrorDialog(capturedError) { + return true; +} + +function logCapturedError(capturedError) { + var logError = showErrorDialog(capturedError); + + // Allow injected showErrorDialog() to prevent default console.error logging. + // This enables renderers like ReactNative to better manage redbox behavior. + if (logError === false) { + return; + } + + var error = capturedError.error; + var suppressLogging = error && error.suppressReactErrorLogging; + if (suppressLogging) { + return; + } + + { + var componentName = capturedError.componentName, + componentStack = capturedError.componentStack, + errorBoundaryName = capturedError.errorBoundaryName, + errorBoundaryFound = capturedError.errorBoundaryFound, + willRetry = capturedError.willRetry; + + var componentNameMessage = componentName + ? "The above error occurred in the <" + componentName + "> component:" + : "The above error occurred in one of your React components:"; + + var errorBoundaryMessage = void 0; + // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow. + if (errorBoundaryFound && errorBoundaryName) { + if (willRetry) { + errorBoundaryMessage = + "React will try to recreate this component tree from scratch " + + ("using the error boundary you provided, " + errorBoundaryName + "."); + } else { + errorBoundaryMessage = + "This error was initially handled by the error boundary " + + errorBoundaryName + + ".\n" + + "Recreating the tree from scratch failed so React will unmount the tree."; + } + } else { + errorBoundaryMessage = + "Consider adding an error boundary to your tree to customize error handling behavior.\n" + + "Visit https://fb.me/react-error-boundaries to learn more about error boundaries."; + } + var combinedMessage = + "" + + componentNameMessage + + componentStack + + "\n\n" + + ("" + errorBoundaryMessage); + + // In development, we provide our own message with just the component stack. + // We don't include the original error message and JS stack because the browser + // has already printed it. Even if the application swallows the error, it is still + // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils. + console.error(combinedMessage); + } +} + var invokeGuardedCallback$3 = ReactErrorUtils.invokeGuardedCallback; var hasCaughtError$1 = ReactErrorUtils.hasCaughtError; var clearCaughtError$1 = ReactErrorUtils.clearCaughtError; -var ReactFiberCommitWork = function(config, captureError) { +function logError(boundary, errorInfo) { + var source = errorInfo.source; + var stack = errorInfo.stack; + if (stack === null) { + stack = getStackAddendumByWorkInProgressFiber(source); + } + + var capturedError = { + componentName: source !== null ? getComponentName(source) : null, + error: errorInfo.value, + errorBoundary: boundary, + componentStack: stack !== null ? stack : "", + errorBoundaryName: null, + errorBoundaryFound: false, + willRetry: false + }; + + if (boundary !== null) { + capturedError.errorBoundaryName = getComponentName(boundary); + capturedError.errorBoundaryFound = capturedError.willRetry = + boundary.tag === ClassComponent; + } else { + capturedError.errorBoundaryName = null; + capturedError.errorBoundaryFound = capturedError.willRetry = false; + } + + 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); + } + } +} + +var ReactFiberCommitWork = function( + config, + captureError, + scheduleWork, + computeExpirationForFiber, + markLegacyErrorBoundaryAsFailed, + recalculateCurrentTime +) { var getPublicInstance = config.getPublicInstance, mutation = config.mutation, persistence = config.persistence; @@ -10185,53 +10245,59 @@ var ReactFiberCommitWork = function(config, captureError) { } } - function commitLifeCycles(current, finishedWork) { + function commitLifeCycles( + finishedRoot, + current, + finishedWork, + currentTime, + committedExpirationTime + ) { switch (finishedWork.tag) { case ClassComponent: { - var instance = finishedWork.stateNode; + var _instance = finishedWork.stateNode; if (finishedWork.effectTag & Update) { if (current === null) { startPhaseTimer(finishedWork, "componentDidMount"); - instance.props = finishedWork.memoizedProps; - instance.state = finishedWork.memoizedState; - instance.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); + _instance.props = finishedWork.memoizedProps; + _instance.state = finishedWork.memoizedState; + _instance.componentDidUpdate(prevProps, prevState); stopPhaseTimer(); } } var updateQueue = finishedWork.updateQueue; if (updateQueue !== null) { - commitCallbacks(updateQueue, instance); + commitCallbacks(updateQueue, _instance); } return; } case HostRoot: { var _updateQueue = finishedWork.updateQueue; if (_updateQueue !== null) { - var _instance = null; + var _instance2 = null; if (finishedWork.child !== null) { switch (finishedWork.child.tag) { case HostComponent: - _instance = getPublicInstance(finishedWork.child.stateNode); + _instance2 = getPublicInstance(finishedWork.child.stateNode); break; case ClassComponent: - _instance = finishedWork.child.stateNode; + _instance2 = finishedWork.child.stateNode; break; } } - commitCallbacks(_updateQueue, _instance); + commitCallbacks(_updateQueue, _instance2); } return; } case HostComponent: { - var _instance2 = finishedWork.stateNode; + var _instance3 = 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). @@ -10240,7 +10306,7 @@ var ReactFiberCommitWork = function(config, captureError) { if (current === null && finishedWork.effectTag & Update) { var type = finishedWork.type; var props = finishedWork.memoizedProps; - commitMount(_instance2, type, props, finishedWork); + commitMount(_instance3, type, props, finishedWork); } return; @@ -10263,17 +10329,78 @@ var ReactFiberCommitWork = function(config, captureError) { } } + function commitErrorLogging(finishedWork, onUncaughtError) { + switch (finishedWork.tag) { + case ClassComponent: + { + var ctor = finishedWork.type; + var _instance4 = finishedWork.stateNode; + var updateQueue = finishedWork.updateQueue; + invariant( + updateQueue !== null && updateQueue.capturedValues !== null, + "An error logging effect should not have been scheduled if no errors " + + "were captured. This error is likely caused by a bug in React. " + + "Please file an issue." + ); + var capturedErrors = updateQueue.capturedValues; + updateQueue.capturedValues = null; + + if (typeof ctor.getDerivedStateFromCatch !== "function") { + // To preserve the preexisting retry behavior of error boundaries, + // we keep track of which ones already failed during this batch. + // This gets reset before we yield back to the browser. + // TODO: Warn in strict mode if getDerivedStateFromCatch is + // not defined. + markLegacyErrorBoundaryAsFailed(_instance4); + } + + _instance4.props = finishedWork.memoizedProps; + _instance4.state = finishedWork.memoizedState; + for (var i = 0; i < capturedErrors.length; i++) { + var errorInfo = capturedErrors[i]; + var _error = errorInfo.value; + logError(finishedWork, errorInfo); + _instance4.componentDidCatch(_error); + } + } + break; + case HostRoot: { + var _updateQueue2 = finishedWork.updateQueue; + invariant( + _updateQueue2 !== null && _updateQueue2.capturedValues !== null, + "An error logging effect should not have been scheduled if no errors " + + "were captured. This error is likely caused by a bug in React. " + + "Please file an issue." + ); + var _capturedErrors = _updateQueue2.capturedValues; + _updateQueue2.capturedValues = null; + for (var _i = 0; _i < _capturedErrors.length; _i++) { + var _errorInfo = _capturedErrors[_i]; + logError(finishedWork, _errorInfo); + onUncaughtError(_errorInfo.value); + } + break; + } + default: + invariant( + false, + "This unit of work tag cannot capture errors. This error is " + + "likely caused by a bug in React. Please file an issue." + ); + } + } + function commitAttachRef(finishedWork) { var ref = finishedWork.ref; if (ref !== null) { - var instance = finishedWork.stateNode; + var _instance5 = finishedWork.stateNode; var instanceToUse = void 0; switch (finishedWork.tag) { case HostComponent: - instanceToUse = getPublicInstance(instance); + instanceToUse = getPublicInstance(_instance5); break; default: - instanceToUse = instance; + instanceToUse = _instance5; } if (typeof ref === "function") { ref(instanceToUse); @@ -10305,9 +10432,9 @@ var ReactFiberCommitWork = function(config, captureError) { switch (current.tag) { case ClassComponent: { safelyDetachRef(current); - var instance = current.stateNode; - if (typeof instance.componentWillUnmount === "function") { - safelyCallComponentWillUnmount(current, instance); + var _instance6 = current.stateNode; + if (typeof _instance6.componentWillUnmount === "function") { + safelyCallComponentWillUnmount(current, _instance6); } return; } @@ -10444,6 +10571,7 @@ var ReactFiberCommitWork = function(config, captureError) { }, commitLifeCycles: commitLifeCycles, + commitErrorLogging: commitErrorLogging, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; @@ -10701,8 +10829,8 @@ var ReactFiberCommitWork = function(config, captureError) { return; } case HostComponent: { - var instance = finishedWork.stateNode; - if (instance != null) { + var _instance7 = finishedWork.stateNode; + if (_instance7 != null) { // Commit the work prepared earlier. var newProps = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps @@ -10715,7 +10843,7 @@ var ReactFiberCommitWork = function(config, captureError) { finishedWork.updateQueue = null; if (updatePayload !== null) { commitUpdate( - instance, + _instance7, updatePayload, type, oldProps, @@ -10765,6 +10893,7 @@ var ReactFiberCommitWork = function(config, captureError) { commitDeletion: commitDeletion, commitWork: commitWork, commitLifeCycles: commitLifeCycles, + commitErrorLogging: commitErrorLogging, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; @@ -11230,73 +11359,6 @@ var ReactFiberInstrumentation = { var ReactFiberInstrumentation_1 = ReactFiberInstrumentation; -// This module is forked in different environments. -// By default, return `true` to log errors to the console. -// Forks can return `false` if this isn't desirable. -function showErrorDialog(capturedError) { - return true; -} - -function logCapturedError(capturedError) { - var logError = showErrorDialog(capturedError); - - // Allow injected showErrorDialog() to prevent default console.error logging. - // This enables renderers like ReactNative to better manage redbox behavior. - if (logError === false) { - return; - } - - var error = capturedError.error; - var suppressLogging = error && error.suppressReactErrorLogging; - if (suppressLogging) { - return; - } - - { - var componentName = capturedError.componentName, - componentStack = capturedError.componentStack, - errorBoundaryName = capturedError.errorBoundaryName, - errorBoundaryFound = capturedError.errorBoundaryFound, - willRetry = capturedError.willRetry; - - var componentNameMessage = componentName - ? "The above error occurred in the <" + componentName + "> component:" - : "The above error occurred in one of your React components:"; - - var errorBoundaryMessage = void 0; - // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow. - if (errorBoundaryFound && errorBoundaryName) { - if (willRetry) { - errorBoundaryMessage = - "React will try to recreate this component tree from scratch " + - ("using the error boundary you provided, " + errorBoundaryName + "."); - } else { - errorBoundaryMessage = - "This error was initially handled by the error boundary " + - errorBoundaryName + - ".\n" + - "Recreating the tree from scratch failed so React will unmount the tree."; - } - } else { - errorBoundaryMessage = - "Consider adding an error boundary to your tree to customize error handling behavior.\n" + - "Visit https://fb.me/react-error-boundaries to learn more about error boundaries."; - } - var combinedMessage = - "" + - componentNameMessage + - componentStack + - "\n\n" + - ("" + errorBoundaryMessage); - - // In development, we provide our own message with just the component stack. - // We don't include the original error message and JS stack because the browser - // has already printed it. Even if the application swallows the error, it is still - // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils. - console.error(combinedMessage); - } -} - var invokeGuardedCallback$2 = ReactErrorUtils.invokeGuardedCallback; var hasCaughtError = ReactErrorUtils.hasCaughtError; var clearCaughtError = ReactErrorUtils.clearCaughtError; @@ -11358,10 +11420,11 @@ var warnAboutInvalidUpdates = void 0; var ReactFiberScheduler = function(config) { var hostContext = ReactFiberHostContext(config); + var popHostContext = hostContext.popHostContext, + popHostContainer = hostContext.popHostContainer; + var hydrationContext = ReactFiberHydrationContext(config); - var popHostContainer = hostContext.popHostContainer, - popHostContext = hostContext.popHostContext, - resetHostContainer = hostContext.resetHostContainer; + var resetHostContainer = hostContext.resetHostContainer; var _ReactFiberBeginWork = ReactFiberBeginWork( config, @@ -11370,8 +11433,7 @@ var ReactFiberScheduler = function(config) { scheduleWork, computeExpirationForFiber ), - beginWork = _ReactFiberBeginWork.beginWork, - beginFailedWork = _ReactFiberBeginWork.beginFailedWork; + beginWork = _ReactFiberBeginWork.beginWork; var _ReactFiberCompleteWo = ReactFiberCompleteWork( config, @@ -11380,12 +11442,28 @@ var ReactFiberScheduler = function(config) { ), completeWork = _ReactFiberCompleteWo.completeWork; - var _ReactFiberCommitWork = ReactFiberCommitWork(config, captureError), + var _ReactFiberUnwindWork = ReactFiberUnwindWork( + hostContext, + scheduleWork, + isAlreadyFailedLegacyErrorBoundary + ), + throwException = _ReactFiberUnwindWork.throwException, + unwindWork = _ReactFiberUnwindWork.unwindWork; + + var _ReactFiberCommitWork = ReactFiberCommitWork( + config, + onCommitPhaseError, + scheduleWork, + computeExpirationForFiber, + markLegacyErrorBoundaryAsFailed, + recalculateCurrentTime + ), commitResetTextContent = _ReactFiberCommitWork.commitResetTextContent, commitPlacement = _ReactFiberCommitWork.commitPlacement, commitDeletion = _ReactFiberCommitWork.commitDeletion, commitWork = _ReactFiberCommitWork.commitWork, commitLifeCycles = _ReactFiberCommitWork.commitLifeCycles, + commitErrorLogging = _ReactFiberCommitWork.commitErrorLogging, commitAttachRef = _ReactFiberCommitWork.commitAttachRef, commitDetachRef = _ReactFiberCommitWork.commitDetachRef; @@ -11397,8 +11475,9 @@ var ReactFiberScheduler = function(config) { // Represents the current time in ms. - var startTime = now(); + var originalStartTimeMs = now(); var mostRecentCurrentTime = msToExpirationTime(0); + var mostRecentCurrentTimeMs = originalStartTimeMs; // Used to ensure computeUniqueAsyncExpiration is monotonically increases. var lastUniqueAsyncExpiration = 0; @@ -11419,32 +11498,70 @@ var ReactFiberScheduler = function(config) { // The next fiber with an effect that we're currently committing. var nextEffect = null; - // Keep track of which fibers have captured an error that need to be handled. - // Work is removed from this collection after componentDidCatch is called. - var capturedErrors = null; - // Keep track of which fibers have failed during the current batch of work. - // This is a different set than capturedErrors, because it is not reset until - // the end of the batch. This is needed to propagate errors correctly if a - // subtree fails more than once. - var failedBoundaries = null; - // Error boundaries that captured an error during the current commit. - var commitPhaseBoundaries = null; - var firstUncaughtError = null; - var didFatal = false; - var isCommitting = false; - var isUnmounting = false; + + var isRootReadyForCommit = false; + + var legacyErrorBoundariesThatAlreadyFailed = null; // Used for performance tracking. var interruptedBy = null; + var stashedWorkInProgressProperties = void 0; + var replayUnitOfWork = void 0; + if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) { + stashedWorkInProgressProperties = null; + replayUnitOfWork = function(failedUnitOfWork, isAsync) { + // Retore the original state of the work-in-progress + Object.assign(failedUnitOfWork, stashedWorkInProgressProperties); + switch (failedUnitOfWork.tag) { + case HostRoot: + popHostContainer(failedUnitOfWork); + popTopLevelContextObject(failedUnitOfWork); + break; + case HostComponent: + popHostContext(failedUnitOfWork); + break; + case ClassComponent: + popContextProvider(failedUnitOfWork); + break; + case HostPortal: + popHostContainer(failedUnitOfWork); + break; + case ContextProvider: + popProvider(failedUnitOfWork); + break; + } + // Replay the begin phase. + invokeGuardedCallback$2(null, workLoop, null, isAsync); + if (hasCaughtError()) { + clearCaughtError(); + } else { + // This should be unreachable because the render phase is + // idempotent + } + }; + } + function resetContextStack() { // Reset the stack reset(); // Reset the cursors resetContext(); - resetProviderStack(); resetHostContainer(); + + // TODO: Unify new context implementation with other stacks + resetProviderStack(); + + { + ReactStrictModeWarnings.discardPendingWarnings(); + } + + nextRoot = null; + nextRenderExpirationTime = NoWork; + nextUnitOfWork = null; + + isRootReadyForCommit = false; } function commitAllHostEffects() { @@ -11470,8 +11587,7 @@ var ReactFiberScheduler = function(config) { // 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); + var primaryEffectTag = effectTag & (Placement | Update | Deletion); switch (primaryEffectTag) { case Placement: { commitPlacement(nextEffect); @@ -11501,9 +11617,7 @@ var ReactFiberScheduler = function(config) { break; } case Deletion: { - isUnmounting = true; commitDeletion(nextEffect); - isUnmounting = false; break; } } @@ -11515,7 +11629,11 @@ var ReactFiberScheduler = function(config) { } } - function commitAllLifeCycles() { + function commitAllLifeCycles( + finishedRoot, + currentTime, + committedExpirationTime + ) { { ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings(); @@ -11523,14 +11641,23 @@ var ReactFiberScheduler = function(config) { ReactStrictModeWarnings.flushPendingDeprecationWarnings(); } } - while (nextEffect !== null) { var effectTag = nextEffect.effectTag; if (effectTag & (Update | Callback)) { recordEffect(); var current = nextEffect.alternate; - commitLifeCycles(current, nextEffect); + commitLifeCycles( + finishedRoot, + current, + nextEffect, + currentTime, + committedExpirationTime + ); + } + + if (effectTag & ErrLog) { + commitErrorLogging(nextEffect, onUncaughtError); } if (effectTag & Ref) { @@ -11538,11 +11665,6 @@ var ReactFiberScheduler = function(config) { commitAttachRef(nextEffect); } - if (effectTag & Err) { - recordEffect(); - commitErrorHandling(nextEffect); - } - var next = nextEffect.nextEffect; // Ensure that we clean these up so that we don't accidentally keep them. // I'm not actually sure this matters because we can't reset firstEffect @@ -11555,11 +11677,22 @@ var ReactFiberScheduler = function(config) { } } + function isAlreadyFailedLegacyErrorBoundary(instance) { + return ( + legacyErrorBoundariesThatAlreadyFailed !== null && + legacyErrorBoundariesThatAlreadyFailed.has(instance) + ); + } + + function markLegacyErrorBoundaryAsFailed(instance) { + if (legacyErrorBoundariesThatAlreadyFailed === null) { + legacyErrorBoundariesThatAlreadyFailed = new Set([instance]); + } else { + legacyErrorBoundariesThatAlreadyFailed.add(instance); + } + } + 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(); @@ -11571,7 +11704,15 @@ var ReactFiberScheduler = function(config) { "related to the return field. This error is likely caused by a bug " + "in React. Please file an issue." ); - root.isReadyForCommit = false; + var committedExpirationTime = root.pendingCommitExpirationTime; + invariant( + committedExpirationTime !== NoWork, + "Cannot commit an incomplete root. This error is likely caused by a " + + "bug in React. Please file an issue." + ); + root.pendingCommitExpirationTime = NoWork; + + var currentTime = recalculateCurrentTime(); // Reset this to null before calling lifecycles ReactCurrentOwner.current = null; @@ -11602,12 +11743,12 @@ var ReactFiberScheduler = function(config) { startCommitHostEffectsTimer(); while (nextEffect !== null) { var didError = false; - var _error = void 0; + var error = void 0; { invokeGuardedCallback$2(null, commitAllHostEffects, null); if (hasCaughtError()) { didError = true; - _error = clearCaughtError(); + error = clearCaughtError(); } } if (didError) { @@ -11616,7 +11757,7 @@ var ReactFiberScheduler = function(config) { "Should have next effect. This error is likely caused by a bug " + "in React. Please file an issue." ); - captureError(nextEffect, _error); + onCommitPhaseError(nextEffect, error); // Clean-up if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; @@ -11641,12 +11782,19 @@ var ReactFiberScheduler = function(config) { startCommitLifeCyclesTimer(); while (nextEffect !== null) { var _didError = false; - var _error2 = void 0; + var _error = void 0; { - invokeGuardedCallback$2(null, commitAllLifeCycles, null); + invokeGuardedCallback$2( + null, + commitAllLifeCycles, + null, + root, + currentTime, + committedExpirationTime + ); if (hasCaughtError()) { _didError = true; - _error2 = clearCaughtError(); + _error = clearCaughtError(); } } if (_didError) { @@ -11655,7 +11803,7 @@ var ReactFiberScheduler = function(config) { "Should have next effect. This error is likely caused by a bug " + "in React. Please file an issue." ); - captureError(nextEffect, _error2); + onCommitPhaseError(nextEffect, _error); if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } @@ -11673,26 +11821,12 @@ var ReactFiberScheduler = function(config) { ReactFiberInstrumentation_1.debugTool.onCommitWork(finishedWork); } - // If we caught any errors during this commit, schedule their boundaries - // to update. - if (commitPhaseBoundaries) { - commitPhaseBoundaries.forEach(scheduleErrorRecovery); - commitPhaseBoundaries = null; - } - - if (firstUncaughtError !== null) { - var _error3 = firstUncaughtError; - firstUncaughtError = null; - onUncaughtError(_error3); - } - var remainingTime = root.current.expirationTime; - if (remainingTime === NoWork) { - capturedErrors = null; - failedBoundaries = null; + // If there's no remaining work, we can clear the set of already failed + // error boundaries. + legacyErrorBoundariesThatAlreadyFailed = null; } - return remainingTime; } @@ -11724,6 +11858,9 @@ var ReactFiberScheduler = function(config) { } function completeUnitOfWork(workInProgress) { + // Attempt to complete the current unit of work, then move to the + // next sibling. If there are no more siblings, return to the + // parent fiber. while (true) { // The current, flushed, state of this fiber is the alternate. // Ideally nothing should rely on this, but relying on it here @@ -11733,80 +11870,140 @@ var ReactFiberScheduler = function(config) { { 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) { + if ((workInProgress.effectTag & Incomplete) === NoEffect) { + // This fiber completed. + var next = completeWork( + current, + workInProgress, + nextRenderExpirationTime + ); stopWorkTimer(workInProgress); + resetExpirationTime(workInProgress, nextRenderExpirationTime); + { + ReactDebugCurrentFiber.resetCurrentFiber(); + } + + if (next !== null) { + stopWorkTimer(workInProgress); + if (true && ReactFiberInstrumentation_1.debugTool) { + ReactFiberInstrumentation_1.debugTool.onCompleteWork( + workInProgress + ); + } + // If completing this work spawned new work, do that next. We'll come + // back here again. + return next; + } + + if ( + returnFiber !== null && + // Do not append effects to parents if a sibling failed to complete + (returnFiber.effectTag & Incomplete) === NoEffect + ) { + // Append all the effects of the subtree and this fiber onto the effect + // list of the parent. The completion order of the children affects the + // side-effect order. + if (returnFiber.firstEffect === null) { + returnFiber.firstEffect = workInProgress.firstEffect; + } + if (workInProgress.lastEffect !== null) { + if (returnFiber.lastEffect !== null) { + returnFiber.lastEffect.nextEffect = workInProgress.firstEffect; + } + returnFiber.lastEffect = workInProgress.lastEffect; + } + + // If this fiber had side-effects, we append it AFTER the children's + // side-effects. We can perform certain side-effects earlier if + // needed, by doing multiple passes over the effect list. We don't want + // to schedule our own side-effect on our own list because if end up + // reusing children we'll schedule this effect onto itself since we're + // at the end. + var effectTag = workInProgress.effectTag; + // Skip both NoWork and PerformedWork tags when creating the effect list. + // PerformedWork effect is read by React DevTools but shouldn't be committed. + if (effectTag > PerformedWork) { + if (returnFiber.lastEffect !== null) { + returnFiber.lastEffect.nextEffect = workInProgress; + } else { + returnFiber.firstEffect = workInProgress; + } + returnFiber.lastEffect = workInProgress; + } + } + if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); } - // If completing this work spawned new work, do that next. We'll come - // back here again. - return next; - } - if (returnFiber !== null) { - // Append all the effects of the subtree and this fiber onto the effect - // list of the parent. The completion order of the children affects the - // side-effect order. - if (returnFiber.firstEffect === null) { - returnFiber.firstEffect = workInProgress.firstEffect; + if (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. + isRootReadyForCommit = true; + return null; } - if (workInProgress.lastEffect !== null) { - if (returnFiber.lastEffect !== null) { - returnFiber.lastEffect.nextEffect = workInProgress.firstEffect; - } - returnFiber.lastEffect = workInProgress.lastEffect; - } - - // If this fiber had side-effects, we append it AFTER the children's - // side-effects. We can perform certain side-effects earlier if - // needed, by doing multiple passes over the effect list. We don't want - // to schedule our own side-effect on our own list because if end up - // reusing children we'll schedule this effect onto itself since we're - // at the end. - var effectTag = workInProgress.effectTag; - // Skip both NoWork and PerformedWork tags when creating the effect list. - // PerformedWork effect is read by React DevTools but shouldn't be committed. - if (effectTag > PerformedWork) { - if (returnFiber.lastEffect !== null) { - returnFiber.lastEffect.nextEffect = workInProgress; - } else { - returnFiber.firstEffect = workInProgress; - } - returnFiber.lastEffect = workInProgress; - } - } - - stopWorkTimer(workInProgress); - if (true && ReactFiberInstrumentation_1.debugTool) { - ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); - } - - if (siblingFiber !== null) { - // If there is more work to do in this returnFiber, do that next. - return siblingFiber; - } else if (returnFiber !== null) { - // If there's no more work in this returnFiber. Complete the returnFiber. - workInProgress = returnFiber; - continue; } else { - // We've reached the root. - var root = workInProgress.stateNode; - root.isReadyForCommit = true; - return null; + // This fiber did not complete because something threw. Pop values off + // the stack without entering the complete phase. If this is a boundary, + // capture values if possible. + var _next = unwindWork(workInProgress); + // Because this fiber did not complete, don't reset its expiration time. + if (workInProgress.effectTag & DidCapture) { + // Restarting an error boundary + stopFailedWorkTimer(workInProgress); + } else { + stopWorkTimer(workInProgress); + } + + { + ReactDebugCurrentFiber.resetCurrentFiber(); + } + + 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. + // Since we're restarting, remove anything that is not a host effect + // from the effect tag. + _next.effectTag &= HostEffectMask; + return _next; + } + + if (returnFiber !== null) { + // Mark the parent fiber as incomplete and clear its effect list. + returnFiber.firstEffect = returnFiber.lastEffect = null; + returnFiber.effectTag |= Incomplete; + } + + if (true && ReactFiberInstrumentation_1.debugTool) { + ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); + } + + if (siblingFiber !== null) { + // If there is more work to do in this returnFiber, do that next. + return siblingFiber; + } else if (returnFiber !== null) { + // If there's no more work in this returnFiber. Complete the returnFiber. + workInProgress = returnFiber; + continue; + } else { + return null; + } } } @@ -11829,45 +12026,11 @@ var ReactFiberScheduler = function(config) { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } + if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) { + stashedWorkInProgressProperties = Object.assign({}, workInProgress); + } var next = beginWork(current, workInProgress, nextRenderExpirationTime); - { - ReactDebugCurrentFiber.resetCurrentFiber(); - } - if (true && ReactFiberInstrumentation_1.debugTool) { - ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); - } - if (next === null) { - // If this doesn't spawn new work, complete the current work. - next = completeUnitOfWork(workInProgress); - } - - ReactCurrentOwner.current = null; - - return next; - } - - function performFailedUnitOfWork(workInProgress) { - { - ReactStrictModeWarnings.discardPendingWarnings(); - } - - // The current, flushed, state of this fiber is the alternate. - // Ideally nothing should rely on this, but relying on it here - // means that we don't need an additional field on the work in - // progress. - var current = workInProgress.alternate; - - // See if beginning this work spawns more work. - startWorkTimer(workInProgress); - { - ReactDebugCurrentFiber.setCurrentFiber(workInProgress); - } - var next = beginFailedWork( - current, - workInProgress, - nextRenderExpirationTime - ); { ReactDebugCurrentFiber.resetCurrentFiber(); } @@ -11886,14 +12049,6 @@ var ReactFiberScheduler = function(config) { } function workLoop(isAsync) { - 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(isAsync); - return; - } if (!isAsync) { // Flush all expired work. while (nextUnitOfWork !== null) { @@ -11907,47 +12062,6 @@ var ReactFiberScheduler = function(config) { } } - function slowWorkLoopThatChecksForFailedWork(isAsync) { - if (!isAsync) { - // Flush all expired work. - while (nextUnitOfWork !== null) { - if (hasCapturedError(nextUnitOfWork)) { - // Use a forked version of performUnitOfWork - nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork); - } else { - nextUnitOfWork = performUnitOfWork(nextUnitOfWork); - } - } - } else { - // Flush asynchronous work until the deadline runs out of time. - while (nextUnitOfWork !== null && !shouldYield()) { - if (hasCapturedError(nextUnitOfWork)) { - // Use a forked version of performUnitOfWork - nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork); - } else { - nextUnitOfWork = performUnitOfWork(nextUnitOfWork); - } - } - } - } - - function renderRootCatchBlock(root, failedWork, boundary, isAsync) { - // We're going to restart the error boundary that captured the error. - // Conceptually, we're unwinding the stack. We need to unwind the - // context stack, too. - unwindContexts(failedWork, boundary); - - // Restart the error boundary using a forked version of - // performUnitOfWork that deletes the boundary's children. The entire - // failed subree will be unmounted. During the commit phase, a special - // lifecycle method is called on the error boundary, which triggers - // a re-render. - nextUnitOfWork = performFailedUnitOfWork(boundary); - - // Continue working. - workLoop(isAsync); - } - function renderRoot(root, expirationTime, isAsync) { invariant( !isWorking, @@ -11956,15 +12070,11 @@ var ReactFiberScheduler = function(config) { ); isWorking = true; - // We're about to mutate the work-in-progress tree. If the root was pending - // commit, it no longer is: we'll need to complete it again. - root.isReadyForCommit = false; - // Check if we're starting from a fresh stack, or if we're resuming from // previously yielded work. if ( - root !== nextRoot || expirationTime !== nextRenderExpirationTime || + root !== nextRoot || nextUnitOfWork === null ) { // Reset the stack and start working from the root. @@ -11974,347 +12084,145 @@ var ReactFiberScheduler = function(config) { nextUnitOfWork = createWorkInProgress( nextRoot.current, null, - expirationTime + nextRenderExpirationTime ); + root.pendingCommitExpirationTime = NoWork; } + var didFatal = false; + startWorkLoopTimer(nextUnitOfWork); - var didError = false; - var error = null; - { - invokeGuardedCallback$2(null, workLoop, null, isAsync); - if (hasCaughtError()) { - didError = true; - error = clearCaughtError(); - } - } - - // An error was thrown during the render phase. - while (didError) { - if (didFatal) { - // This was a fatal error. Don't attempt to recover from it. - firstUncaughtError = error; - break; - } - - var failedWork = nextUnitOfWork; - if (failedWork === null) { - // An error was thrown but there's no current unit of work. This can - // happen during the commit phase if there's a bug in the renderer. - didFatal = true; - continue; - } - - // "Capture" the error by finding the nearest boundary. If there is no - // error boundary, we use the root. - var boundary = captureError(failedWork, error); - invariant( - boundary !== null, - "Should have found an error boundary. This error is likely " + - "caused by a bug in React. Please file an issue." - ); - - 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, - isAsync - ); - if (hasCaughtError()) { - didError = true; - error = clearCaughtError(); - continue; + do { + try { + workLoop(isAsync); + } catch (thrownValue) { + if (nextUnitOfWork === null) { + // This is a fatal error. + didFatal = true; + onUncaughtError(thrownValue); + break; } - } - // We're finished working. Exit the error loop. - break; - } - var uncaughtError = firstUncaughtError; + if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) { + var failedUnitOfWork = nextUnitOfWork; + replayUnitOfWork(failedUnitOfWork, isAsync); + } + + var sourceFiber = nextUnitOfWork; + var returnFiber = sourceFiber["return"]; + if (returnFiber === null) { + // This is a fatal error. + didFatal = true; + onUncaughtError(thrownValue); + break; + } + throwException(returnFiber, sourceFiber, thrownValue); + nextUnitOfWork = completeUnitOfWork(sourceFiber); + } + break; + } while (true); // We're done performing work. Time to clean up. stopWorkLoopTimer(interruptedBy); interruptedBy = null; isWorking = false; - didFatal = false; - firstUncaughtError = null; - if (uncaughtError !== null) { - onUncaughtError(uncaughtError); - } - - return root.isReadyForCommit ? root.current.alternate : null; - } - - // Returns the boundary that captured the error, or null if the error is ignored - function captureError(failedWork, error) { - // It is no longer valid because we exited the user code. - ReactCurrentOwner.current = null; - { - ReactDebugCurrentFiber.resetCurrentFiber(); - } - - // Search for the nearest error boundary. - var boundary = null; - - // Passed to logCapturedError() - var errorBoundaryFound = false; - var willRetry = false; - var errorBoundaryName = null; - - // Host containers are a special case. If the failed work itself is a host - // container, then it acts as its own boundary. In all other cases, we - // ignore the work itself and only search through the parents. - if (failedWork.tag === HostRoot) { - boundary = failedWork; - - if (isFailedBoundary(failedWork)) { - // If this root already failed, there must have been an error when - // attempting to unmount it. This is a worst-case scenario and - // should only be possible if there's a bug in the renderer. - didFatal = true; - } - } else { - var node = failedWork["return"]; - while (node !== null && boundary === null) { - if (node.tag === ClassComponent) { - var instance = node.stateNode; - if (typeof instance.componentDidCatch === "function") { - errorBoundaryFound = true; - errorBoundaryName = getComponentName(node); - - // Found an error boundary! - boundary = node; - willRetry = true; - } - } else if (node.tag === HostRoot) { - // Treat the root like a no-op error boundary - boundary = node; - } - - if (isFailedBoundary(node)) { - // This boundary is already in a failed state. - - // If we're currently unmounting, that means this error was - // thrown while unmounting a failed subtree. We should ignore - // the error. - if (isUnmounting) { - return null; - } - - // If we're in the commit phase, we should check to see if - // this boundary already captured an error during this commit. - // This case exists because multiple errors can be thrown during - // a single commit without interruption. - if ( - commitPhaseBoundaries !== null && - (commitPhaseBoundaries.has(node) || - (node.alternate !== null && - commitPhaseBoundaries.has(node.alternate))) - ) { - // If so, we should ignore this error. - return null; - } - - // The error should propagate to the next boundary -— we keep looking. - boundary = null; - willRetry = false; - } - - node = node["return"]; - } - } - - if (boundary !== null) { - // Add to the collection of failed boundaries. This lets us know that - // subsequent errors in this subtree should propagate to the next boundary. - if (failedBoundaries === null) { - failedBoundaries = new Set(); - } - failedBoundaries.add(boundary); - - // This method is unsafe outside of the begin and complete phases. - // We might be in the commit phase when an error is captured. - // The risk is that the return path from this Fiber may not be accurate. - // That risk is acceptable given the benefit of providing users more context. - var _componentStack = getStackAddendumByWorkInProgressFiber(failedWork); - var _componentName = getComponentName(failedWork); - - // Add to the collection of captured errors. This is stored as a global - // map of errors and their component stack location keyed by the boundaries - // that capture them. We mostly use this Map as a Set; it's a Map only to - // avoid adding a field to Fiber to store the error. - if (capturedErrors === null) { - capturedErrors = new Map(); - } - - var capturedError = { - componentName: _componentName, - componentStack: _componentStack, - error: error, - errorBoundary: errorBoundaryFound ? boundary.stateNode : null, - errorBoundaryFound: errorBoundaryFound, - errorBoundaryName: errorBoundaryName, - willRetry: willRetry - }; - - capturedErrors.set(boundary, capturedError); - - try { - logCapturedError(capturedError); - } catch (e) { - // Prevent cycle if logCapturedError() throws. - // A cycle may still occur if logCapturedError renders a component that throws. - var suppressLogging = e && e.suppressReactErrorLogging; - if (!suppressLogging) { - console.error(e); - } - } - - // If we're in the commit phase, defer scheduling an update on the - // boundary until after the commit is complete - if (isCommitting) { - if (commitPhaseBoundaries === null) { - commitPhaseBoundaries = new Set(); - } - commitPhaseBoundaries.add(boundary); + // Yield back to main thread. + if (didFatal) { + // There was a fatal error. + return null; + } else if (nextUnitOfWork === null) { + // We reached the root. + if (isRootReadyForCommit) { + // The root successfully completed. It's ready for commit. + root.pendingCommitExpirationTime = expirationTime; + var finishedWork = root.current.alternate; + return finishedWork; } else { - // Otherwise, schedule an update now. - // TODO: Is this actually necessary during the render phase? Is it - // possible to unwind and continue rendering at the same priority, - // without corrupting internal state? - scheduleErrorRecovery(boundary); - } - return boundary; - } else if (firstUncaughtError === null) { - // If no boundary is found, we'll need to throw the error - firstUncaughtError = error; - } - return null; - } - - function hasCapturedError(fiber) { - // TODO: capturedErrors should store the boundary instance, to avoid needing - // to check the alternate. - return ( - capturedErrors !== null && - (capturedErrors.has(fiber) || - (fiber.alternate !== null && capturedErrors.has(fiber.alternate))) - ); - } - - function isFailedBoundary(fiber) { - // TODO: failedBoundaries should store the boundary instance, to avoid - // needing to check the alternate. - return ( - failedBoundaries !== null && - (failedBoundaries.has(fiber) || - (fiber.alternate !== null && failedBoundaries.has(fiber.alternate))) - ); - } - - function commitErrorHandling(effectfulFiber) { - var capturedError = void 0; - if (capturedErrors !== null) { - capturedError = capturedErrors.get(effectfulFiber); - capturedErrors["delete"](effectfulFiber); - if (capturedError == null) { - if (effectfulFiber.alternate !== null) { - effectfulFiber = effectfulFiber.alternate; - capturedError = capturedErrors.get(effectfulFiber); - capturedErrors["delete"](effectfulFiber); - } - } - } - - 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: + // The root did not complete. invariant( false, - "Invalid type of work. This error is likely caused by a bug in " + - "React. Please file an issue." + "Expired work should have completed. This error is likely caused " + + "by a bug in React. Please file an issue." ); + } + } else { + // There's more work to do, but we ran out of time. Yield back to + // the renderer. + return null; } } - function unwindContexts(from, to) { - var node = from; - while (node !== null) { - switch (node.tag) { + function scheduleCapture(sourceFiber, boundaryFiber, value, expirationTime) { + // TODO: We only support dispatching errors. + var capturedValue = createCapturedValue(value, sourceFiber); + var update = { + expirationTime: expirationTime, + partialState: null, + callback: null, + isReplace: false, + isForced: false, + capturedValue: capturedValue, + next: null + }; + insertUpdateIntoFiber(boundaryFiber, update); + scheduleWork(boundaryFiber, expirationTime); + } + + function dispatch(sourceFiber, value, expirationTime) { + invariant( + !isWorking || isCommitting, + "dispatch: Cannot dispatch during the render phase." + ); + + // TODO: Handle arrays + + var fiber = sourceFiber["return"]; + while (fiber !== null) { + switch (fiber.tag) { case ClassComponent: - popContextProvider(node); - break; - case HostComponent: - popHostContext(node); + var ctor = fiber.type; + var instance = fiber.stateNode; + if ( + typeof ctor.getDerivedStateFromCatch === "function" || + (typeof instance.componentDidCatch === "function" && + !isAlreadyFailedLegacyErrorBoundary(instance)) + ) { + scheduleCapture(sourceFiber, fiber, value, expirationTime); + return; + } break; + // TODO: Handle async boundaries case HostRoot: - popHostContainer(node); - break; - case HostPortal: - popHostContainer(node); - break; - case ContextProvider: - popProvider(node); - break; + scheduleCapture(sourceFiber, fiber, value, expirationTime); + return; } - if (node === to || node.alternate === to) { - stopFailedWorkTimer(node); - break; - } else { - stopWorkTimer(node); - } - node = node["return"]; + fiber = fiber["return"]; + } + + if (sourceFiber.tag === HostRoot) { + // Error was thrown at the root. There is no parent, so the root + // itself should capture it. + scheduleCapture(sourceFiber, sourceFiber, value, expirationTime); } } - function computeAsyncExpiration() { + function onCommitPhaseError(fiber, error) { + return dispatch(fiber, error, Sync); + } + + function computeAsyncExpiration(currentTime) { // 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; + var expirationMs = 5000; + var bucketSizeMs = 250; return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs); } - function computeInteractiveExpiration() { + function computeInteractiveExpiration(currentTime) { // Should complete within ~500ms. 600ms max. - var currentTime = recalculateCurrentTime(); var expirationMs = 500; var bucketSizeMs = 100; return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs); @@ -12322,7 +12230,8 @@ var ReactFiberScheduler = function(config) { // Creates a unique async expiration time. function computeUniqueAsyncExpiration() { - var result = computeAsyncExpiration(); + var currentTime = recalculateCurrentTime(); + var result = computeAsyncExpiration(currentTime); if (result <= lastUniqueAsyncExpiration) { // Since we assume the current time monotonically increases, we only hit // this branch when computeUniqueAsyncExpiration is fired multiple times @@ -12354,10 +12263,12 @@ var ReactFiberScheduler = function(config) { if (fiber.mode & AsyncMode) { if (isBatchingInteractiveUpdates) { // This is an interactive update - expirationTime = computeInteractiveExpiration(); + var currentTime = recalculateCurrentTime(); + expirationTime = computeInteractiveExpiration(currentTime); } else { // This is an async update - expirationTime = computeAsyncExpiration(); + var _currentTime = recalculateCurrentTime(); + expirationTime = computeAsyncExpiration(_currentTime); } } else { // This is a sync update @@ -12382,23 +12293,6 @@ var ReactFiberScheduler = function(config) { 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(); @@ -12430,10 +12324,27 @@ var ReactFiberScheduler = function(config) { if (node["return"] === null) { if (node.tag === HostRoot) { var root = node.stateNode; - - checkRootNeedsClearing(root, fiber, expirationTime); - requestWork(root, expirationTime); - checkRootNeedsClearing(root, fiber, expirationTime); + if ( + !isWorking && + nextRenderExpirationTime !== NoWork && + expirationTime < nextRenderExpirationTime + ) { + // This is an interruption. (Used for performance tracking.) + interruptedBy = fiber; + resetContextStack(); + } + if (nextRoot !== root || !isWorking) { + 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." + ); + } } else { { if (!isErrorRecovery && fiber.tag === ClassComponent) { @@ -12447,27 +12358,23 @@ var ReactFiberScheduler = function(config) { } } - function scheduleErrorRecovery(fiber) { - scheduleWorkImpl(fiber, Sync, true); - } - function recalculateCurrentTime() { // Subtract initial time so it fits inside 32bits - var ms = now() - startTime; - mostRecentCurrentTime = msToExpirationTime(ms); + mostRecentCurrentTimeMs = now() - originalStartTimeMs; + mostRecentCurrentTime = msToExpirationTime(mostRecentCurrentTimeMs); return mostRecentCurrentTime; } function deferredUpdates(fn) { var previousExpirationContext = expirationContext; - expirationContext = computeAsyncExpiration(); + var currentTime = recalculateCurrentTime(); + expirationContext = computeAsyncExpiration(currentTime); try { return fn(); } finally { expirationContext = previousExpirationContext; } } - function syncUpdates(fn, a, b, c, d) { var previousExpirationContext = expirationContext; expirationContext = Sync; @@ -12525,7 +12432,7 @@ var ReactFiberScheduler = function(config) { } // Compute a timeout for the given expiration time. - var currentMs = now() - startTime; + var currentMs = now() - originalStartTimeMs; var expirationMs = expirationTimeToMs(expirationTime); var timeout = expirationMs - currentMs; @@ -12538,16 +12445,35 @@ var ReactFiberScheduler = function(config) { // 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." - ); + addRootToSchedule(root, expirationTime); + + if (isRendering) { + // Prevent reentrancy. Remaining work will be scheduled at the end of + // the currently rendering batch. + return; } + if (isBatchingUpdates) { + // Flush work at the end of the batch. + if (isUnbatchingUpdates) { + // ...unless we're inside unbatchedUpdates, in which case we should + // flush it now. + nextFlushedRoot = root; + nextFlushedExpirationTime = Sync; + performWorkOnRoot(root, Sync, false); + } + return; + } + + // TODO: Get rid of Sync and use current time? + if (expirationTime === Sync) { + performSyncWork(); + } else { + scheduleCallbackWithExpiration(expirationTime); + } + } + + function addRootToSchedule(root, expirationTime) { // Add the root to the schedule. // Check if this root is already part of the schedule. if (root.nextScheduledRoot === null) { @@ -12572,37 +12498,11 @@ var ReactFiberScheduler = function(config) { root.remainingExpirationTime = expirationTime; } } - - if (isRendering) { - // Prevent reentrancy. Remaining work will be scheduled at the end of - // the currently rendering batch. - return; - } - - if (isBatchingUpdates) { - if (isUnbatchingUpdates) { - // Flush work at the end of the batch. - // ...unless we're inside unbatchedUpdates, in which case we should - // flush it now. - nextFlushedRoot = root; - nextFlushedExpirationTime = Sync; - performWorkOnRoot(root, Sync, false); - } - return; - } - - // TODO: Get rid of Sync and use current time? - if (expirationTime === Sync) { - performSyncWork(); - } else { - scheduleCallbackWithExpiration(expirationTime); - } } function findHighestPriorityRoot() { var highestPriorityWork = NoWork; var highestPriorityRoot = null; - if (lastScheduledRoot !== null) { var previousScheduledRoot = lastScheduledRoot; var root = firstScheduledRoot; @@ -12664,7 +12564,8 @@ var ReactFiberScheduler = function(config) { var previousFlushedRoot = nextFlushedRoot; if ( previousFlushedRoot !== null && - previousFlushedRoot === highestPriorityRoot + previousFlushedRoot === highestPriorityRoot && + highestPriorityWork === Sync ) { nestedUpdateCount++; } else { @@ -12739,7 +12640,6 @@ var ReactFiberScheduler = function(config) { // Clean-up. deadline = null; deadlineDidExpire = false; - nestedUpdateCount = 0; finishRendering(); } @@ -12758,6 +12658,8 @@ var ReactFiberScheduler = function(config) { } function finishRendering() { + nestedUpdateCount = 0; + if (completedBatches !== null) { var batches = completedBatches; completedBatches = null; @@ -12775,10 +12677,10 @@ var ReactFiberScheduler = function(config) { } if (hasUnhandledError) { - var _error4 = unhandledError; + var error = unhandledError; unhandledError = null; hasUnhandledError = false; - throw _error4; + throw error; } } @@ -12871,8 +12773,6 @@ var ReactFiberScheduler = function(config) { return true; } - // TODO: Not happy about this hook. Conceptually, renderRoot should return a - // tuple of (isReadyForCommit, didError, error) function onUncaughtError(error) { invariant( nextFlushedRoot !== null, @@ -12989,6 +12889,7 @@ var ReactFiberScheduler = function(config) { } return { + recalculateCurrentTime: recalculateCurrentTime, computeExpirationForFiber: computeExpirationForFiber, scheduleWork: scheduleWork, requestWork: requestWork, @@ -13032,6 +12933,7 @@ var ReactFiberReconciler$1 = function(config) { var _ReactFiberScheduler = ReactFiberScheduler(config), computeUniqueAsyncExpiration = _ReactFiberScheduler.computeUniqueAsyncExpiration, + recalculateCurrentTime = _ReactFiberScheduler.recalculateCurrentTime, computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber, scheduleWork = _ReactFiberScheduler.scheduleWork, requestWork = _ReactFiberScheduler.requestWork, @@ -13045,7 +12947,13 @@ var ReactFiberReconciler$1 = function(config) { interactiveUpdates = _ReactFiberScheduler.interactiveUpdates, flushInteractiveUpdates = _ReactFiberScheduler.flushInteractiveUpdates; - function scheduleRootUpdate(current, element, expirationTime, callback) { + function scheduleRootUpdate( + current, + element, + currentTime, + expirationTime, + callback + ) { { if ( ReactDebugCurrentFiber.phase === "render" && @@ -13080,6 +12988,7 @@ var ReactFiberReconciler$1 = function(config) { callback: callback, isReplace: false, isForced: false, + capturedValue: null, next: null }; insertUpdateIntoFiber(current, update); @@ -13092,6 +13001,7 @@ var ReactFiberReconciler$1 = function(config) { element, container, parentComponent, + currentTime, expirationTime, callback ) { @@ -13117,7 +13027,13 @@ var ReactFiberReconciler$1 = function(config) { container.pendingContext = context; } - return scheduleRootUpdate(current, element, expirationTime, callback); + return scheduleRootUpdate( + current, + element, + currentTime, + expirationTime, + callback + ); } function findHostInstance(fiber) { @@ -13134,17 +13050,34 @@ var ReactFiberReconciler$1 = function(config) { }, updateContainer: function(element, container, parentComponent, callback) { var current = container.current; + var currentTime = recalculateCurrentTime(); var expirationTime = computeExpirationForFiber(current); return updateContainerAtExpirationTime( element, container, parentComponent, + currentTime, + expirationTime, + callback + ); + }, + updateContainerAtExpirationTime: function( + element, + container, + parentComponent, + expirationTime, + callback + ) { + var currentTime = recalculateCurrentTime(); + return updateContainerAtExpirationTime( + element, + container, + parentComponent, + currentTime, expirationTime, callback ); }, - - updateContainerAtExpirationTime: updateContainerAtExpirationTime, flushRoot: flushRoot, @@ -13226,6 +13159,48 @@ var reactReconciler = ReactFiberReconciler$3["default"] ? ReactFiberReconciler$3["default"] : ReactFiberReconciler$3; +/** + * Keeps track of allocating and associating native "tags" which are numeric, + * unique view IDs. All the native tags are negative numbers, to avoid + * collisions, but in the JS we keep track of them as positive integers to store + * them effectively in Arrays. So we must refer to them as "inverses" of the + * native tags (that are * normally negative). + * + * It *must* be the case that every `rootNodeID` always maps to the exact same + * `tag` forever. The easiest way to accomplish this is to never delete + * anything from this table. + * Why: Because `dangerouslyReplaceNodeWithMarkupByID` relies on being able to + * unmount a component with a `rootNodeID`, then mount a new one in its place, + */ +var INITIAL_TAG_COUNT = 1; +var ReactNativeTagHandles = { + tagsStartAt: INITIAL_TAG_COUNT, + tagCount: INITIAL_TAG_COUNT, + + allocateTag: function() { + // Skip over root IDs as those are reserved for native + while (this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount)) { + ReactNativeTagHandles.tagCount++; + } + var tag = ReactNativeTagHandles.tagCount; + ReactNativeTagHandles.tagCount++; + return tag; + }, + + assertRootTag: function(tag) { + invariant( + this.reactTagIsNativeTopRootID(tag), + "Expect a native root tag, instead got %s", + tag + ); + }, + + reactTagIsNativeTopRootID: function(reactTag) { + // We reserve all tags that are 1 mod 10 for native root views + return reactTag % 10 === 1; + } +}; + function _classCallCheck$2(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); @@ -13661,8 +13636,7 @@ var ReactFabric = { NativeMethodsMixin: NativeMethodsMixin, // Used by react-native-github/Libraries/ components ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin, // requireNativeComponent - ReactGlobalSharedState: ReactGlobalSharedState, // Systrace - ReactNativeComponentTree: ReactNativeComponentTree, // InspectorUtils, ScrollResponder + ReactNativeComponentTree: ReactNativeComponentTree, // ScrollResponder ReactNativePropRegistry: ReactNativePropRegistry, // flattenStyle, Stylesheet TouchHistoryMath: TouchHistoryMath, // PanResponder createReactNativeComponentClass: createReactNativeComponentClass, // RCTText, RCTView, ReactNativeART diff --git a/Libraries/Renderer/ReactFabric-prod.js b/Libraries/Renderer/ReactFabric-prod.js index e25c7162a16..9605c98520c 100644 --- a/Libraries/Renderer/ReactFabric-prod.js +++ b/Libraries/Renderer/ReactFabric-prod.js @@ -13,80 +13,15 @@ require("InitializeCore"); var invariant = require("fbjs/lib/invariant"), emptyFunction = require("fbjs/lib/emptyFunction"), - RCTEventEmitter = require("RCTEventEmitter"), UIManager = require("UIManager"), - React = require("react"), TextInputState = require("TextInputState"), deepDiffer = require("deepDiffer"), flattenStyle = require("flattenStyle"), + React = require("react"), emptyObject = require("fbjs/lib/emptyObject"), shallowEqual = require("fbjs/lib/shallowEqual"), - FabricUIManager = require("FabricUIManager"); -function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) { - this._hasCaughtError = !1; - this._caughtError = null; - var funcArgs = Array.prototype.slice.call(arguments, 3); - try { - func.apply(context, funcArgs); - } catch (error) { - (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; - ReactErrorUtils._rethrowError = null; - ReactErrorUtils._hasRethrowError = !1; - throw error; - } -} -var eventPluginOrder = null, + FabricUIManager = require("FabricUIManager"), + eventPluginOrder = null, namesToPlugins = {}; function recomputePluginOrdering() { if (eventPluginOrder) @@ -175,17 +110,6 @@ function isMoveish(topLevelType) { function isStartish(topLevelType) { return "topMouseDown" === topLevelType || "topTouchStart" === topLevelType; } -function executeDispatch(event, simulated, listener, inst) { - simulated = event.type || "unknown-event"; - event.currentTarget = getNodeFromInstance(inst); - ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError( - simulated, - listener, - void 0, - event - ); - event.currentTarget = null; -} function executeDirectDispatch(event) { var dispatchListener = event._dispatchListeners, dispatchInstance = event._dispatchInstances; @@ -218,26 +142,6 @@ function accumulateInto(current, next) { function forEachAccumulated(arr, cb, scope) { Array.isArray(arr) ? arr.forEach(cb, scope) : arr && cb.call(scope, arr); } -var eventQueue = null; -function executeDispatchesAndReleaseTopLevel(e) { - if (e) { - var dispatchListeners = e._dispatchListeners, - dispatchInstances = e._dispatchInstances; - if (Array.isArray(dispatchListeners)) - for ( - var i = 0; - i < dispatchListeners.length && !e.isPropagationStopped(); - i++ - ) - executeDispatch(e, !1, dispatchListeners[i], dispatchInstances[i]); - else - dispatchListeners && - executeDispatch(e, !1, dispatchListeners, dispatchInstances); - e._dispatchListeners = null; - e._dispatchInstances = null; - e.isPersistent() || e.constructor.release(e); - } -} var injection = { injectEventPluginOrder: function(injectedEventPluginOrder) { invariant( @@ -942,6 +846,7 @@ var eventTypes = { nativeEvent, nativeEventTarget ) { + if (null == targetInst) return null; var bubbleDispatchConfig = customBubblingEventTypes[topLevelType], directDispatchConfig = customDirectEventTypes[topLevelType]; invariant( @@ -987,161 +892,28 @@ function getInstanceFromTag(tag) { return "number" === typeof tag ? instanceCache[tag] || null : tag; } var ReactNativeComponentTree = Object.freeze({ - precacheFiberNode: function(hostInst, tag) { - instanceCache[tag] = hostInst; - }, - uncacheFiberNode: function(tag) { - delete instanceCache[tag]; - delete instanceProps[tag]; - }, - getClosestInstanceFromNode: getInstanceFromTag, - getInstanceFromNode: getInstanceFromTag, - getNodeFromInstance: function(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; - }, - updateFiberProps: function(tag, props) { - instanceProps[tag] = props; - } - }), - restoreTarget = null, - restoreQueue = null; -function restoreStateOfTarget(target) { - if ((target = getInstanceFromNode(target))) { - invariant( - null, - "Fiber needs to be injected to handle a fiber target for controlled events. This error is likely caused by a bug in React. Please file an issue." - ); - var props = getFiberCurrentPropsFromNode(target.stateNode); - null.restoreControlledState(target.stateNode, target.type, props); - } -} -function _batchedUpdates(fn, bookkeeping) { - return fn(bookkeeping); -} -function _flushInteractiveUpdates() {} -var isBatching = !1; -function batchedUpdates(fn, bookkeeping) { - if (isBatching) return fn(bookkeeping); - isBatching = !0; - try { - return _batchedUpdates(fn, bookkeeping); - } finally { - if (((isBatching = !1), null !== restoreTarget || null !== restoreQueue)) - if ( - (_flushInteractiveUpdates(), - restoreTarget && - ((bookkeeping = restoreTarget), - (fn = restoreQueue), - (restoreQueue = restoreTarget = null), - restoreStateOfTarget(bookkeeping), - fn)) - ) - for (bookkeeping = 0; bookkeeping < fn.length; bookkeeping++) - restoreStateOfTarget(fn[bookkeeping]); - } -} -var ReactNativeTagHandles = { - tagsStartAt: 1, - tagCount: 1, - allocateTag: function() { - for (; this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount); ) - ReactNativeTagHandles.tagCount++; - var tag = ReactNativeTagHandles.tagCount; - ReactNativeTagHandles.tagCount++; - return tag; - }, - assertRootTag: function(tag) { - invariant( - this.reactTagIsNativeTopRootID(tag), - "Expect a native root tag, instead got %s", - tag - ); - }, - reactTagIsNativeTopRootID: function(reactTag) { - return 1 === reactTag % 10; - } + precacheFiberNode: function(hostInst, tag) { + instanceCache[tag] = hostInst; }, - EMPTY_NATIVE_EVENT = {}; -function _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam) { - var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT, - inst = getInstanceFromTag(rootNodeID); - batchedUpdates(function() { - 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({ - getListener: getListener, - registrationNames: registrationNameModules, - _receiveRootNodeIDEvent: _receiveRootNodeIDEvent, - receiveEvent: function(rootNodeID, topLevelType, nativeEventParam) { - _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam); + uncacheFiberNode: function(tag) { + delete instanceCache[tag]; + delete instanceProps[tag]; }, - receiveTouches: function(eventTopLevelType, touches, changedIndices) { - if ( - "topTouchEnd" === eventTopLevelType || - "topTouchCancel" === eventTopLevelType - ) { - var JSCompiler_temp = []; - for (var i = 0; i < changedIndices.length; i++) { - var index = changedIndices[i]; - JSCompiler_temp.push(touches[index]); - touches[index] = null; - } - for (i = changedIndices = 0; i < touches.length; i++) - (index = touches[i]), - null !== index && (touches[changedIndices++] = index); - touches.length = changedIndices; - } else - for (JSCompiler_temp = [], i = 0; i < changedIndices.length; i++) - JSCompiler_temp.push(touches[changedIndices[i]]); - for ( - changedIndices = 0; - changedIndices < JSCompiler_temp.length; - changedIndices++ - ) { - i = JSCompiler_temp[changedIndices]; - i.changedTouches = JSCompiler_temp; - i.touches = touches; - index = null; - var target = i.target; - null === target || - void 0 === target || - target < ReactNativeTagHandles.tagsStartAt || - (index = target); - _receiveRootNodeIDEvent(index, eventTopLevelType, i); - } + getClosestInstanceFromNode: getInstanceFromTag, + getInstanceFromNode: getInstanceFromTag, + getNodeFromInstance: function(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; + }, + updateFiberProps: function(tag, props) { + instanceProps[tag] = props; } }); -// TODO: This event emitter is interfering with the existing ReactNative renderer. -// RCTEventEmitter.register(ReactNativeEventEmitter); injection.injectEventPluginOrder([ "ResponderEventPlugin", "ReactNativeBridgeEventPlugin" @@ -1193,7 +965,24 @@ function createPortal(children, containerInfo, implementation) { implementation: implementation }; } -var TouchHistoryMath = { +var restoreTarget = null, + restoreQueue = null; +function restoreStateOfTarget(target) { + if ((target = getInstanceFromNode(target))) { + invariant( + null, + "Fiber needs to be injected to handle a fiber target for controlled events. This error is likely caused by a bug in React. Please file an issue." + ); + var props = getFiberCurrentPropsFromNode(target.stateNode); + null.restoreControlledState(target.stateNode, target.type, props); + } +} +function _batchedUpdates(fn, bookkeeping) { + return fn(bookkeeping); +} +function _flushInteractiveUpdates() {} +var isBatching = !1, + TouchHistoryMath = { centroidDimension: function( touchHistory, touchesChangedAfter, @@ -1294,12 +1083,6 @@ var TouchHistoryMath = { }, noCentroid: -1 }, - ReactCurrentOwner = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, - ReactGlobalSharedState = Object.freeze({ - ReactCurrentOwner: ReactCurrentOwner, - ReactDebugCurrentFrame: null - }), objects = {}, uniqueID = 1, emptyObject$2 = {}, @@ -1561,6 +1344,8 @@ function mountSafeCallback(context, callback) { } }; } +var ReactCurrentOwner = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner; function getComponentName(fiber) { fiber = fiber.type; if ("function" === typeof fiber) return fiber.displayName || fiber.name; @@ -1856,6 +1641,39 @@ function push(cursor, value) { valueStack[index] = cursor.current; cursor.current = value; } +function getStackAddendumByWorkInProgressFiber(workInProgress) { + var info = ""; + do { + a: switch (workInProgress.tag) { + case 0: + case 1: + case 2: + case 5: + var owner = workInProgress._debugOwner, + source = workInProgress._debugSource; + var JSCompiler_inline_result = getComponentName(workInProgress); + var ownerName = null; + owner && (ownerName = getComponentName(owner)); + owner = source; + JSCompiler_inline_result = + "\n in " + + (JSCompiler_inline_result || "Unknown") + + (owner + ? " (at " + + owner.fileName.replace(/^.*[\\\/]/, "") + + ":" + + owner.lineNumber + + ")" + : ownerName ? " (created by " + ownerName + ")" : ""); + break a; + default: + JSCompiler_inline_result = ""; + } + info += JSCompiler_inline_result; + workInProgress = workInProgress["return"]; + } while (workInProgress); + return info; +} new Set(); var contextStackCursor = { current: emptyObject }, didPerformWorkStackCursor = { current: !1 }, @@ -2113,7 +1931,8 @@ function createUpdateQueue(baseState) { last: null, callbackList: null, hasForceUpdate: !1, - isInitialized: !1 + isInitialized: !1, + capturedValues: null }; } function insertUpdateIntoQueue(queue, update) { @@ -2126,7 +1945,10 @@ function insertUpdateIntoQueue(queue, update) { ) queue.expirationTime = update.expirationTime; } -function insertUpdateIntoFiber(fiber, update) { +var q1 = void 0, + q2 = void 0; +function ensureUpdateQueues(fiber) { + q1 = q2 = null; var alternateFiber = fiber.alternate, queue1 = fiber.updateQueue; null === queue1 && (queue1 = fiber.updateQueue = createUpdateQueue(null)); @@ -2135,13 +1957,19 @@ function insertUpdateIntoFiber(fiber, update) { null === fiber && (fiber = alternateFiber.updateQueue = createUpdateQueue(null))) : (fiber = null); - fiber = fiber !== queue1 ? fiber : null; - null === fiber - ? insertUpdateIntoQueue(queue1, update) - : null === queue1.last || null === fiber.last - ? (insertUpdateIntoQueue(queue1, update), - insertUpdateIntoQueue(fiber, update)) - : (insertUpdateIntoQueue(queue1, update), (fiber.last = update)); + q1 = queue1; + q2 = fiber !== queue1 ? fiber : null; +} +function insertUpdateIntoFiber(fiber, update) { + ensureUpdateQueues(fiber); + fiber = q1; + var queue2 = q2; + null === queue2 + ? insertUpdateIntoQueue(fiber, update) + : null === fiber.last || null === queue2.last + ? (insertUpdateIntoQueue(fiber, update), + insertUpdateIntoQueue(queue2, update)) + : (insertUpdateIntoQueue(fiber, update), (queue2.last = update)); } function getStateFromUpdate(update, instance, prevState, props) { update = update.partialState; @@ -2165,6 +1993,7 @@ function processUpdateQueue( first: queue.first, last: queue.last, isInitialized: queue.isInitialized, + capturedValues: queue.capturedValues, callbackList: null, hasForceUpdate: !1 }); @@ -2212,6 +2041,11 @@ function processUpdateQueue( null === updateExpirationTime && (updateExpirationTime = queue.callbackList = []), updateExpirationTime.push(update)); + null !== update.capturedValue && + ((updateExpirationTime = queue.capturedValues), + null === updateExpirationTime + ? (queue.capturedValues = [update.capturedValue]) + : updateExpirationTime.push(update.capturedValue)); } update = update.next; } @@ -2219,6 +2053,7 @@ function processUpdateQueue( ? (workInProgress.effectTag |= 32) : null !== queue.first || queue.hasForceUpdate || + null !== queue.capturedValues || (workInProgress.updateQueue = null); didSkip || (queue.baseState = current); return current; @@ -2248,11 +2083,48 @@ function ReactFiberClassComponent( memoizeProps, memoizeState ) { + function checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldState, + newState, + newContext + ) { + if ( + null === oldProps || + (null !== workInProgress.updateQueue && + workInProgress.updateQueue.hasForceUpdate) + ) + return !0; + var instance = workInProgress.stateNode; + workInProgress = workInProgress.type; + return "function" === typeof instance.shouldComponentUpdate + ? instance.shouldComponentUpdate(newProps, newState, newContext) + : workInProgress.prototype && + workInProgress.prototype.isPureReactComponent + ? !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) + : !0; + } function adoptClassInstance(workInProgress, instance) { instance.updater = updater; workInProgress.stateNode = instance; instance._reactInternalFiber = workInProgress; } + function callComponentWillReceiveProps( + workInProgress, + instance, + newProps, + newContext + ) { + workInProgress = instance.state; + "function" === typeof instance.componentWillReceiveProps && + instance.componentWillReceiveProps(newProps, newContext); + "function" === typeof instance.UNSAFE_componentWillReceiveProps && + instance.UNSAFE_componentWillReceiveProps(newProps, newContext); + instance.state !== workInProgress && + updater.enqueueReplaceState(instance, instance.state, null); + } function callGetDerivedStateFromProps(workInProgress, instance, props) { instance = workInProgress.type; if ("function" === typeof instance.getDerivedStateFromProps) @@ -2274,7 +2146,7 @@ function ReactFiberClassComponent( callback: callback, isReplace: !1, isForced: !1, - nextCallback: null, + capturedValue: null, next: null }); scheduleWork(instance, expirationTime); @@ -2289,7 +2161,7 @@ function ReactFiberClassComponent( callback: callback, isReplace: !0, isForced: !1, - nextCallback: null, + capturedValue: null, next: null }); scheduleWork(instance, expirationTime); @@ -2304,7 +2176,7 @@ function ReactFiberClassComponent( callback: callback, isReplace: !1, isForced: !0, - nextCallback: null, + capturedValue: null, next: null }); scheduleWork(instance, expirationTime); @@ -2341,7 +2213,8 @@ function ReactFiberClassComponent( return ctor; }, mountClassInstance: function(workInProgress, renderExpirationTime) { - var current = workInProgress.alternate, + var ctor = workInProgress.type, + current = workInProgress.alternate, instance = workInProgress.stateNode, props = workInProgress.pendingProps, unmaskedContext = getUnmaskedContext(workInProgress); @@ -2351,20 +2224,20 @@ function ReactFiberClassComponent( instance.context = getMaskedContext(workInProgress, unmaskedContext); ("function" !== typeof instance.UNSAFE_componentWillMount && "function" !== typeof instance.componentWillMount) || - "function" === typeof workInProgress.type.getDerivedStateFromProps || - ((unmaskedContext = instance.state), + "function" === typeof ctor.getDerivedStateFromProps || + ((ctor = instance.state), "function" === typeof instance.componentWillMount && instance.componentWillMount(), "function" === typeof instance.UNSAFE_componentWillMount && instance.UNSAFE_componentWillMount(), - unmaskedContext !== instance.state && + ctor !== instance.state && updater.enqueueReplaceState(instance, instance.state, null), - (unmaskedContext = workInProgress.updateQueue), - null !== unmaskedContext && + (ctor = workInProgress.updateQueue), + null !== ctor && (instance.state = processUpdateQueue( current, workInProgress, - unmaskedContext, + ctor, instance, props, renderExpirationTime @@ -2372,12 +2245,9 @@ function ReactFiberClassComponent( "function" === typeof instance.componentDidMount && (workInProgress.effectTag |= 4); }, - updateClassInstance: function( - current, - workInProgress, - renderExpirationTime - ) { - var instance = workInProgress.stateNode; + resumeMountClassInstance: function(workInProgress, renderExpirationTime) { + var ctor = workInProgress.type, + instance = workInProgress.stateNode; instance.props = workInProgress.memoizedProps; instance.state = workInProgress.memoizedState; var oldProps = workInProgress.memoizedProps, @@ -2387,21 +2257,114 @@ function ReactFiberClassComponent( newUnmaskedContext = getMaskedContext(workInProgress, newUnmaskedContext); ("function" !== typeof instance.UNSAFE_componentWillReceiveProps && "function" !== typeof instance.componentWillReceiveProps) || - "function" === typeof workInProgress.type.getDerivedStateFromProps || - (oldProps === newProps && oldContext === newUnmaskedContext) || - ((oldContext = instance.state), - "function" === typeof instance.componentWillReceiveProps && - instance.componentWillReceiveProps(newProps, newUnmaskedContext), - "function" === typeof instance.UNSAFE_componentWillReceiveProps && - instance.UNSAFE_componentWillReceiveProps( + "function" === typeof ctor.getDerivedStateFromProps || + ((oldProps !== newProps || oldContext !== newUnmaskedContext) && + callComponentWillReceiveProps( + workInProgress, + instance, newProps, newUnmaskedContext - ), - instance.state !== oldContext && - updater.enqueueReplaceState(instance, instance.state, null)); - var partialState = void 0; + )); + oldContext = void 0; oldProps !== newProps && - (partialState = callGetDerivedStateFromProps( + (oldContext = callGetDerivedStateFromProps( + workInProgress, + instance, + newProps + )); + var oldState = workInProgress.memoizedState; + renderExpirationTime = + null !== workInProgress.updateQueue + ? processUpdateQueue( + null, + workInProgress, + workInProgress.updateQueue, + instance, + newProps, + renderExpirationTime + ) + : oldState; + null !== oldContext && + void 0 !== oldContext && + (renderExpirationTime = + null === renderExpirationTime || void 0 === renderExpirationTime + ? oldContext + : Object.assign({}, renderExpirationTime, oldContext)); + if ( + !( + oldProps !== newProps || + oldState !== renderExpirationTime || + didPerformWorkStackCursor.current || + (null !== workInProgress.updateQueue && + workInProgress.updateQueue.hasForceUpdate) + ) + ) + return ( + "function" === typeof instance.componentDidMount && + (workInProgress.effectTag |= 4), + !1 + ); + (oldProps = checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldState, + renderExpirationTime, + newUnmaskedContext + )) + ? (("function" !== typeof instance.UNSAFE_componentWillUpdate && + "function" !== typeof instance.componentWillUpdate) || + "function" === typeof ctor.getDerivedStateFromProps || + ("function" === typeof instance.componentWillUpdate && + instance.componentWillUpdate( + newProps, + renderExpirationTime, + newUnmaskedContext + ), + "function" === typeof instance.UNSAFE_componentWillUpdate && + instance.UNSAFE_componentWillUpdate( + newProps, + renderExpirationTime, + newUnmaskedContext + )), + "function" === typeof instance.componentDidUpdate && + (workInProgress.effectTag |= 4)) + : ("function" === typeof instance.componentDidMount && + (workInProgress.effectTag |= 4), + memoizeProps(workInProgress, newProps), + memoizeState(workInProgress, renderExpirationTime)); + instance.props = newProps; + instance.state = renderExpirationTime; + instance.context = newUnmaskedContext; + return oldProps; + }, + updateClassInstance: function( + current, + workInProgress, + renderExpirationTime + ) { + var ctor = workInProgress.type, + instance = workInProgress.stateNode; + instance.props = workInProgress.memoizedProps; + instance.state = workInProgress.memoizedState; + var oldProps = workInProgress.memoizedProps, + newProps = workInProgress.pendingProps, + oldContext = instance.context, + newUnmaskedContext = getUnmaskedContext(workInProgress); + newUnmaskedContext = getMaskedContext(workInProgress, newUnmaskedContext); + ("function" !== typeof instance.UNSAFE_componentWillReceiveProps && + "function" !== typeof instance.componentWillReceiveProps) || + "function" === typeof ctor.getDerivedStateFromProps || + ((oldProps !== newProps || oldContext !== newUnmaskedContext) && + callComponentWillReceiveProps( + workInProgress, + instance, + newProps, + newUnmaskedContext + )); + var derivedStateFromProps = void 0; + oldProps !== newProps && + (derivedStateFromProps = callGetDerivedStateFromProps( workInProgress, instance, newProps @@ -2418,12 +2381,12 @@ function ReactFiberClassComponent( renderExpirationTime ) : oldContext; - null !== partialState && - void 0 !== partialState && + null !== derivedStateFromProps && + void 0 !== derivedStateFromProps && (renderExpirationTime = null === renderExpirationTime || void 0 === renderExpirationTime - ? partialState - : Object.assign({}, renderExpirationTime, partialState)); + ? derivedStateFromProps + : Object.assign({}, renderExpirationTime, derivedStateFromProps)); if ( !( oldProps !== newProps || @@ -2440,33 +2403,17 @@ function ReactFiberClassComponent( (workInProgress.effectTag |= 4), !1 ); - partialState = renderExpirationTime; - if ( - null === oldProps || - (null !== workInProgress.updateQueue && - workInProgress.updateQueue.hasForceUpdate) - ) - partialState = !0; - else { - var instance$jscomp$0 = workInProgress.stateNode, - type = workInProgress.type; - partialState = - "function" === typeof instance$jscomp$0.shouldComponentUpdate - ? instance$jscomp$0.shouldComponentUpdate( - newProps, - partialState, - newUnmaskedContext - ) - : type.prototype && type.prototype.isPureReactComponent - ? !shallowEqual(oldProps, newProps) || - !shallowEqual(oldContext, partialState) - : !0; - } - partialState + (derivedStateFromProps = checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldContext, + renderExpirationTime, + newUnmaskedContext + )) ? (("function" !== typeof instance.UNSAFE_componentWillUpdate && "function" !== typeof instance.componentWillUpdate) || - "function" === - typeof workInProgress.type.getDerivedStateFromProps || + "function" === typeof ctor.getDerivedStateFromProps || ("function" === typeof instance.componentWillUpdate && instance.componentWillUpdate( newProps, @@ -2490,7 +2437,7 @@ function ReactFiberClassComponent( instance.props = newProps; instance.state = renderExpirationTime; instance.context = newUnmaskedContext; - return partialState; + return derivedStateFromProps; } }; } @@ -3200,7 +3147,19 @@ function ReactFiberBeginWork( computeExpirationForFiber ) { function reconcileChildren(current, workInProgress, nextChildren) { - var renderExpirationTime = workInProgress.expirationTime; + reconcileChildrenAtExpirationTime( + current, + workInProgress, + nextChildren, + workInProgress.expirationTime + ); + } + function reconcileChildrenAtExpirationTime( + current, + workInProgress, + nextChildren, + renderExpirationTime + ) { workInProgress.child = null === current ? mountChildFibers( @@ -3228,19 +3187,34 @@ function ReactFiberBeginWork( current, workInProgress, shouldUpdate, - hasContext + hasContext, + didCaptureError, + renderExpirationTime ) { markRef(current, workInProgress); - if (!shouldUpdate) + if (!shouldUpdate && !didCaptureError) return ( hasContext && invalidateContextProvider(workInProgress, !1), bailoutOnAlreadyFinishedWork(current, workInProgress) ); shouldUpdate = workInProgress.stateNode; ReactCurrentOwner.current = workInProgress; - var nextChildren = shouldUpdate.render(); + var nextChildren = didCaptureError ? null : shouldUpdate.render(); workInProgress.effectTag |= 1; - reconcileChildren(current, workInProgress, nextChildren); + didCaptureError && + (reconcileChildrenAtExpirationTime( + current, + workInProgress, + null, + renderExpirationTime + ), + (workInProgress.child = null)); + reconcileChildrenAtExpirationTime( + current, + workInProgress, + nextChildren, + renderExpirationTime + ); workInProgress.memoizedState = shouldUpdate.state; workInProgress.memoizedProps = shouldUpdate.props; hasContext && invalidateContextProvider(workInProgress, !0); @@ -3340,25 +3314,6 @@ function ReactFiberBeginWork( } return workInProgress.child; } - function bailoutOnLowPriority(current, workInProgress) { - switch (workInProgress.tag) { - case 3: - pushHostRootContext(workInProgress); - break; - case 2: - pushContextProvider(workInProgress); - break; - case 4: - pushHostContainer( - workInProgress, - workInProgress.stateNode.containerInfo - ); - break; - case 13: - pushProvider(workInProgress); - } - return null; - } var shouldSetTextContent = config.shouldSetTextContent, shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree, pushHostContext = hostContext.pushHostContext, @@ -3381,14 +3336,32 @@ function ReactFiberBeginWork( callGetDerivedStateFromProps = config.callGetDerivedStateFromProps, constructClassInstance = config.constructClassInstance, mountClassInstance = config.mountClassInstance, + resumeMountClassInstance = config.resumeMountClassInstance, updateClassInstance = config.updateClassInstance; return { beginWork: function(current, workInProgress, renderExpirationTime) { if ( 0 === workInProgress.expirationTime || workInProgress.expirationTime > renderExpirationTime - ) - return bailoutOnLowPriority(current, workInProgress); + ) { + switch (workInProgress.tag) { + case 3: + pushHostRootContext(workInProgress); + break; + case 2: + pushContextProvider(workInProgress); + break; + case 4: + pushHostContainer( + workInProgress, + workInProgress.stateNode.containerInfo + ); + break; + case 13: + pushProvider(workInProgress); + } + return null; + } switch (workInProgress.tag) { case 0: invariant( @@ -3429,7 +3402,9 @@ function ReactFiberBeginWork( current, workInProgress, !0, - props + props, + !1, + renderExpirationTime ))) : ((workInProgress.tag = 1), reconcileChildren(current, workInProgress, fn), @@ -3456,92 +3431,117 @@ function ReactFiberBeginWork( current ); case 2: - return ( - (props = pushContextProvider(workInProgress)), - (fn = void 0), - null === current - ? workInProgress.stateNode - ? invariant(!1, "Resuming work not yet implemented.") - : (constructClassInstance( - workInProgress, - workInProgress.pendingProps - ), - mountClassInstance(workInProgress, renderExpirationTime), - (fn = !0)) - : (fn = updateClassInstance( - current, + props = pushContextProvider(workInProgress); + null === current + ? null === workInProgress.stateNode + ? (constructClassInstance( + workInProgress, + workInProgress.pendingProps + ), + mountClassInstance(workInProgress, renderExpirationTime), + (fn = !0)) + : (fn = resumeMountClassInstance( workInProgress, renderExpirationTime - )), - finishClassComponent(current, workInProgress, fn, props) + )) + : (fn = updateClassInstance( + current, + workInProgress, + renderExpirationTime + )); + unmaskedContext = !1; + var updateQueue = workInProgress.updateQueue; + null !== updateQueue && + null !== updateQueue.capturedValues && + (unmaskedContext = fn = !0); + return finishClassComponent( + current, + workInProgress, + fn, + props, + unmaskedContext, + renderExpirationTime ); case 3: - return ( - pushHostRootContext(workInProgress), - (props = workInProgress.updateQueue), - null !== props - ? ((fn = workInProgress.memoizedState), - (props = processUpdateQueue( - current, + a: if ( + (pushHostRootContext(workInProgress), + (fn = workInProgress.updateQueue), + null !== fn) + ) { + unmaskedContext = workInProgress.memoizedState; + props = processUpdateQueue( + current, + workInProgress, + fn, + null, + null, + renderExpirationTime + ); + workInProgress.memoizedState = props; + fn = workInProgress.updateQueue; + if (null !== fn && null !== fn.capturedValues) fn = null; + else if (unmaskedContext === props) { + resetHydrationState(); + current = bailoutOnAlreadyFinishedWork(current, workInProgress); + break a; + } else fn = props.element; + unmaskedContext = workInProgress.stateNode; + (null === current || null === current.child) && + unmaskedContext.hydrate && + enterHydrationState(workInProgress) + ? ((workInProgress.effectTag |= 2), + (workInProgress.child = mountChildFibers( workInProgress, - props, - null, null, + fn, renderExpirationTime - )), - fn === props - ? (resetHydrationState(), - (current = bailoutOnAlreadyFinishedWork( - current, - workInProgress - ))) - : ((fn = props.element), - (unmaskedContext = workInProgress.stateNode), - (null === current || null === current.child) && - unmaskedContext.hydrate && - enterHydrationState(workInProgress) - ? ((workInProgress.effectTag |= 2), - (workInProgress.child = mountChildFibers( - workInProgress, - null, - fn, - renderExpirationTime - ))) - : (resetHydrationState(), - reconcileChildren(current, workInProgress, fn)), - (workInProgress.memoizedState = props), - (current = workInProgress.child))) + ))) : (resetHydrationState(), - (current = bailoutOnAlreadyFinishedWork( - current, - workInProgress - ))), - current - ); + reconcileChildren(current, workInProgress, fn)); + workInProgress.memoizedState = props; + current = workInProgress.child; + } else + resetHydrationState(), + (current = bailoutOnAlreadyFinishedWork(current, workInProgress)); + return current; case 5: - pushHostContext(workInProgress); - null === current && tryToClaimNextHydratableInstance(workInProgress); - props = workInProgress.type; - var memoizedProps = workInProgress.memoizedProps; - fn = workInProgress.pendingProps; - unmaskedContext = null !== current ? current.memoizedProps : null; - didPerformWorkStackCursor.current || memoizedProps !== fn - ? ((memoizedProps = fn.children), - shouldSetTextContent(props, fn) - ? (memoizedProps = null) - : unmaskedContext && - shouldSetTextContent(props, unmaskedContext) && - (workInProgress.effectTag |= 16), - markRef(current, workInProgress), - 1073741823 !== renderExpirationTime && - workInProgress.mode & 1 && - shouldDeprioritizeSubtree(props, fn) - ? ((workInProgress.expirationTime = 1073741823), - (current = null)) - : (reconcileChildren(current, workInProgress, memoizedProps), - (workInProgress.memoizedProps = fn), - (current = workInProgress.child))) - : (current = bailoutOnAlreadyFinishedWork(current, workInProgress)); + a: { + pushHostContext(workInProgress); + null === current && + tryToClaimNextHydratableInstance(workInProgress); + props = workInProgress.type; + updateQueue = workInProgress.memoizedProps; + fn = workInProgress.pendingProps; + unmaskedContext = null !== current ? current.memoizedProps : null; + if (!didPerformWorkStackCursor.current && updateQueue === fn) { + if ( + (updateQueue = + workInProgress.mode & 1 && + shouldDeprioritizeSubtree(props, fn)) + ) + workInProgress.expirationTime = 1073741823; + if (!updateQueue || 1073741823 !== renderExpirationTime) { + current = bailoutOnAlreadyFinishedWork(current, workInProgress); + break a; + } + } + updateQueue = fn.children; + shouldSetTextContent(props, fn) + ? (updateQueue = null) + : unmaskedContext && + shouldSetTextContent(props, unmaskedContext) && + (workInProgress.effectTag |= 16); + markRef(current, workInProgress); + 1073741823 !== renderExpirationTime && + workInProgress.mode & 1 && + shouldDeprioritizeSubtree(props, fn) + ? ((workInProgress.expirationTime = 1073741823), + (workInProgress.memoizedProps = fn), + (current = null)) + : (reconcileChildren(current, workInProgress, updateQueue), + (workInProgress.memoizedProps = fn), + (current = workInProgress.child)); + } return current; case 6: return ( @@ -3646,28 +3646,28 @@ function ReactFiberBeginWork( unmaskedContext = workInProgress.memoizedProps; if (didPerformWorkStackCursor.current || unmaskedContext !== fn) { workInProgress.memoizedProps = fn; - memoizedProps = fn.value; - if (null === unmaskedContext) memoizedProps = 1073741823; + updateQueue = fn.value; + if (null === unmaskedContext) updateQueue = 1073741823; else { var oldValue = unmaskedContext.value; - (oldValue === memoizedProps && - (0 !== oldValue || 1 / oldValue === 1 / memoizedProps)) || - (oldValue !== oldValue && memoizedProps !== memoizedProps) - ? (memoizedProps = 0) - : ((memoizedProps = + (oldValue === updateQueue && + (0 !== oldValue || 1 / oldValue === 1 / updateQueue)) || + (oldValue !== oldValue && updateQueue !== updateQueue) + ? (updateQueue = 0) + : ((updateQueue = "function" === typeof props.calculateChangedBits - ? props.calculateChangedBits(oldValue, memoizedProps) + ? props.calculateChangedBits(oldValue, updateQueue) : 1073741823), - (memoizedProps |= 0), - 0 !== memoizedProps && + (updateQueue |= 0), + 0 !== updateQueue && propagateContextChange( workInProgress, props, - memoizedProps, + updateQueue, renderExpirationTime )); } - workInProgress.stateNode = memoizedProps; + workInProgress.stateNode = updateQueue; pushProvider(workInProgress); null !== unmaskedContext && unmaskedContext.children === fn.children ? (current = bailoutOnAlreadyFinishedWork( @@ -3685,12 +3685,12 @@ function ReactFiberBeginWork( unmaskedContext = workInProgress.type; fn = workInProgress.pendingProps; props = unmaskedContext.currentValue; - memoizedProps = unmaskedContext.changedBits; - 0 !== memoizedProps && + updateQueue = unmaskedContext.changedBits; + 0 !== updateQueue && propagateContextChange( workInProgress, unmaskedContext, - memoizedProps, + updateQueue, renderExpirationTime ); renderExpirationTime = fn.observedBits; @@ -3698,6 +3698,11 @@ function ReactFiberBeginWork( renderExpirationTime = 1073741823; workInProgress.stateNode = renderExpirationTime; renderExpirationTime = fn.children; + "function" !== typeof renderExpirationTime && + invariant( + !1, + "A context consumer was rendered with multiple children, or a child that isn't a function. A context consumer expects a single child that is a function. If you did pass a function, make sure there is no trailing or leading whitespace around it." + ); renderExpirationTime = renderExpirationTime(props); reconcileChildren(current, workInProgress, renderExpirationTime); return workInProgress.child; @@ -3707,47 +3712,6 @@ function ReactFiberBeginWork( "Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue." ); } - }, - beginFailedWork: function(current, workInProgress, renderExpirationTime) { - switch (workInProgress.tag) { - case 2: - pushContextProvider(workInProgress); - break; - case 3: - pushHostRootContext(workInProgress); - break; - default: - invariant( - !1, - "Invalid type of work. This error is likely caused by a bug in React. Please file an issue." - ); - } - workInProgress.effectTag |= 64; - null === current - ? (workInProgress.child = null) - : workInProgress.child !== current.child && - (workInProgress.child = current.child); - if ( - 0 === workInProgress.expirationTime || - workInProgress.expirationTime > renderExpirationTime - ) - return bailoutOnLowPriority(current, workInProgress); - workInProgress.firstEffect = null; - workInProgress.lastEffect = null; - workInProgress.child = - null === current - ? mountChildFibers(workInProgress, null, null, renderExpirationTime) - : reconcileChildFibers( - workInProgress, - current.child, - null, - renderExpirationTime - ); - 2 === workInProgress.tag && - ((current = workInProgress.stateNode), - (workInProgress.memoizedProps = current.props), - (workInProgress.memoizedState = current.state)); - return workInProgress.child; } }; } @@ -3804,30 +3768,26 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { if (null !== workInProgress.firstEffect) { var container = portalOrRoot.containerInfo, newChildSet = createContainerChildSet(container); - finalizeContainerChildren(container, newChildSet) && - markUpdate(workInProgress); - portalOrRoot.pendingChildren = newChildSet; - a: for (portalOrRoot = workInProgress.child; null !== portalOrRoot; ) { - if (5 === portalOrRoot.tag || 6 === portalOrRoot.tag) - appendChildToContainerChildSet(newChildSet, portalOrRoot.stateNode); - else if (4 !== portalOrRoot.tag && null !== portalOrRoot.child) { - portalOrRoot.child["return"] = portalOrRoot; - portalOrRoot = portalOrRoot.child; + a: for (var node = workInProgress.child; null !== node; ) { + if (5 === node.tag || 6 === node.tag) + appendChildToContainerChildSet(newChildSet, node.stateNode); + else if (4 !== node.tag && null !== node.child) { + node.child["return"] = node; + node = node.child; continue; } - if (portalOrRoot === workInProgress) break a; - for (; null === portalOrRoot.sibling; ) { - if ( - null === portalOrRoot["return"] || - portalOrRoot["return"] === workInProgress - ) + if (node === workInProgress) break a; + for (; null === node.sibling; ) { + if (null === node["return"] || node["return"] === workInProgress) break a; - portalOrRoot = portalOrRoot["return"]; + node = node["return"]; } - portalOrRoot.sibling["return"] = portalOrRoot["return"]; - portalOrRoot = portalOrRoot.sibling; + node.sibling["return"] = node["return"]; + node = node.sibling; } + portalOrRoot.pendingChildren = newChildSet; markUpdate(workInProgress); + finalizeContainerChildren(container, newChildSet); } }; updateHostComponent = function( @@ -3886,7 +3846,18 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { case 1: return null; case 2: - return popContextProvider(workInProgress), null; + return ( + popContextProvider(workInProgress), + (current = workInProgress.stateNode), + (newProps = workInProgress.updateQueue), + null !== newProps && + null !== newProps.capturedValues && + ((workInProgress.effectTag &= -65), + "function" === typeof current.componentDidCatch + ? (workInProgress.effectTag |= 256) + : (newProps.capturedValues = null)), + null + ); case 3: popHostContainer(workInProgress); pop(didPerformWorkStackCursor, workInProgress); @@ -3898,6 +3869,10 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { if (null === current || null === current.child) popHydrationState(workInProgress), (workInProgress.effectTag &= -3); updateHostContainer(workInProgress); + current = workInProgress.updateQueue; + null !== current && + null !== current.capturedValues && + (workInProgress.effectTag |= 256); return null; case 5: popHostContext(workInProgress); @@ -3905,10 +3880,10 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { var type = workInProgress.type; if (null !== current && null != workInProgress.stateNode) { var oldProps = current.memoizedProps, - instance = workInProgress.stateNode, + _instance = workInProgress.stateNode, currentHostContext = getHostContext(); - instance = prepareUpdate( - instance, + _instance = prepareUpdate( + _instance, type, oldProps, newProps, @@ -3918,7 +3893,7 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { updateHostComponent( current, workInProgress, - instance, + _instance, type, oldProps, newProps, @@ -4066,7 +4041,102 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { } }; } -function ReactFiberCommitWork(config, captureError) { +function ReactFiberUnwindWork( + hostContext, + scheduleWork, + isAlreadyFailedLegacyErrorBoundary +) { + var popHostContainer = hostContext.popHostContainer, + popHostContext = hostContext.popHostContext; + return { + throwException: function(returnFiber, sourceFiber, rawValue) { + sourceFiber.effectTag |= 512; + sourceFiber.firstEffect = sourceFiber.lastEffect = null; + sourceFiber = { + value: rawValue, + source: sourceFiber, + stack: getStackAddendumByWorkInProgressFiber(sourceFiber) + }; + do { + switch (returnFiber.tag) { + case 3: + ensureUpdateQueues(returnFiber); + returnFiber.updateQueue.capturedValues = [sourceFiber]; + returnFiber.effectTag |= 1024; + return; + case 2: + if ( + ((rawValue = returnFiber.stateNode), + 0 === (returnFiber.effectTag & 64) && + null !== rawValue && + "function" === typeof rawValue.componentDidCatch && + !isAlreadyFailedLegacyErrorBoundary(rawValue)) + ) { + ensureUpdateQueues(returnFiber); + rawValue = returnFiber.updateQueue; + var capturedValues = rawValue.capturedValues; + null === capturedValues + ? (rawValue.capturedValues = [sourceFiber]) + : capturedValues.push(sourceFiber); + returnFiber.effectTag |= 1024; + return; + } + } + returnFiber = returnFiber["return"]; + } while (null !== returnFiber); + }, + unwindWork: function(workInProgress) { + switch (workInProgress.tag) { + case 2: + popContextProvider(workInProgress); + var effectTag = workInProgress.effectTag; + return effectTag & 1024 + ? ((workInProgress.effectTag = (effectTag & -1025) | 64), + workInProgress) + : null; + case 3: + return ( + popHostContainer(workInProgress), + pop(didPerformWorkStackCursor, workInProgress), + pop(contextStackCursor, workInProgress), + (effectTag = workInProgress.effectTag), + effectTag & 1024 + ? ((workInProgress.effectTag = (effectTag & -1025) | 64), + workInProgress) + : null + ); + case 5: + return popHostContext(workInProgress), null; + case 4: + return popHostContainer(workInProgress), null; + case 13: + return popProvider(workInProgress), null; + default: + return null; + } + } + }; +} +function logError(boundary, errorInfo) { + var source = errorInfo.source; + null === errorInfo.stack && getStackAddendumByWorkInProgressFiber(source); + null !== source && getComponentName(source); + errorInfo = errorInfo.value; + null !== boundary && getComponentName(boundary); + try { + (errorInfo && errorInfo.suppressReactErrorLogging) || + console.error(errorInfo); + } catch (e) { + (e && e.suppressReactErrorLogging) || console.error(e); + } +} +function ReactFiberCommitWork( + config, + captureError, + scheduleWork, + computeExpirationForFiber, + markLegacyErrorBoundaryAsFailed +) { function safelyDetachRef(current) { var ref = current.ref; if (null !== ref) @@ -4078,46 +4148,46 @@ function ReactFiberCommitWork(config, captureError) { } else ref.value = null; } - function commitLifeCycles(current, finishedWork) { + function commitLifeCycles(finishedRoot, current, finishedWork) { switch (finishedWork.tag) { case 2: - var instance = finishedWork.stateNode; + finishedRoot = finishedWork.stateNode; if (finishedWork.effectTag & 4) if (null === current) - (instance.props = finishedWork.memoizedProps), - (instance.state = finishedWork.memoizedState), - instance.componentDidMount(); + (finishedRoot.props = finishedWork.memoizedProps), + (finishedRoot.state = finishedWork.memoizedState), + finishedRoot.componentDidMount(); else { var prevProps = current.memoizedProps; current = current.memoizedState; - instance.props = finishedWork.memoizedProps; - instance.state = finishedWork.memoizedState; - instance.componentDidUpdate(prevProps, current); + finishedRoot.props = finishedWork.memoizedProps; + finishedRoot.state = finishedWork.memoizedState; + finishedRoot.componentDidUpdate(prevProps, current); } finishedWork = finishedWork.updateQueue; - null !== finishedWork && commitCallbacks(finishedWork, instance); + null !== finishedWork && commitCallbacks(finishedWork, finishedRoot); break; case 3: - instance = finishedWork.updateQueue; - if (null !== instance) { - current = null; + current = finishedWork.updateQueue; + if (null !== current) { + finishedRoot = null; if (null !== finishedWork.child) switch (finishedWork.child.tag) { case 5: - current = getPublicInstance(finishedWork.child.stateNode); + finishedRoot = getPublicInstance(finishedWork.child.stateNode); break; case 2: - current = finishedWork.child.stateNode; + finishedRoot = finishedWork.child.stateNode; } - commitCallbacks(instance, current); + commitCallbacks(current, finishedRoot); } break; case 5: - instance = finishedWork.stateNode; + finishedRoot = finishedWork.stateNode; null === current && finishedWork.effectTag & 4 && commitMount( - instance, + finishedRoot, finishedWork.type, finishedWork.memoizedProps, finishedWork @@ -4134,16 +4204,59 @@ function ReactFiberCommitWork(config, captureError) { ); } } + function commitErrorLogging(finishedWork, onUncaughtError) { + switch (finishedWork.tag) { + case 2: + var ctor = finishedWork.type; + onUncaughtError = finishedWork.stateNode; + var updateQueue = finishedWork.updateQueue; + invariant( + null !== updateQueue && null !== updateQueue.capturedValues, + "An error logging effect should not have been scheduled if no errors were captured. This error is likely caused by a bug in React. Please file an issue." + ); + var capturedErrors = updateQueue.capturedValues; + updateQueue.capturedValues = null; + "function" !== typeof ctor.getDerivedStateFromCatch && + markLegacyErrorBoundaryAsFailed(onUncaughtError); + onUncaughtError.props = finishedWork.memoizedProps; + onUncaughtError.state = finishedWork.memoizedState; + for (ctor = 0; ctor < capturedErrors.length; ctor++) { + updateQueue = capturedErrors[ctor]; + var _error = updateQueue.value; + logError(finishedWork, updateQueue); + onUncaughtError.componentDidCatch(_error); + } + break; + case 3: + ctor = finishedWork.updateQueue; + invariant( + null !== ctor && null !== ctor.capturedValues, + "An error logging effect should not have been scheduled if no errors were captured. This error is likely caused by a bug in React. Please file an issue." + ); + capturedErrors = ctor.capturedValues; + ctor.capturedValues = null; + for (ctor = 0; ctor < capturedErrors.length; ctor++) + (updateQueue = capturedErrors[ctor]), + logError(finishedWork, updateQueue), + onUncaughtError(updateQueue.value); + break; + default: + invariant( + !1, + "This unit of work tag cannot capture errors. This error is likely caused by a bug in React. Please file an issue." + ); + } + } function commitAttachRef(finishedWork) { var ref = finishedWork.ref; if (null !== ref) { - var instance = finishedWork.stateNode; + var _instance5 = finishedWork.stateNode; switch (finishedWork.tag) { case 5: - finishedWork = getPublicInstance(instance); + finishedWork = getPublicInstance(_instance5); break; default: - finishedWork = instance; + finishedWork = _instance5; } "function" === typeof ref ? ref(finishedWork) @@ -4162,12 +4275,12 @@ function ReactFiberCommitWork(config, captureError) { switch (current.tag) { case 2: safelyDetachRef(current); - var instance = current.stateNode; - if ("function" === typeof instance.componentWillUnmount) + var _instance6 = current.stateNode; + if ("function" === typeof _instance6.componentWillUnmount) try { - (instance.props = current.memoizedProps), - (instance.state = current.memoizedState), - instance.componentWillUnmount(); + (_instance6.props = current.memoizedProps), + (_instance6.state = current.memoizedState), + _instance6.componentWillUnmount(); } catch (unmountError) { captureError(current, unmountError); } @@ -4245,6 +4358,7 @@ function ReactFiberCommitWork(config, captureError) { commitContainer(finishedWork); }, commitLifeCycles: commitLifeCycles, + commitErrorLogging: commitErrorLogging, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; @@ -4459,55 +4573,101 @@ function ReactFiberHydrationContext(config) { }; } function ReactFiberScheduler(config) { + function resetContextStack() { + for (; -1 < index; ) (valueStack[index] = null), index--; + previousContext = emptyObject; + contextStackCursor.current = emptyObject; + didPerformWorkStackCursor.current = !1; + resetHostContainer(); + for (var i = index$1; -1 < i; i--) { + var context = stack[i].type.context; + context.currentValue = context.defaultValue; + context.changedBits = 0; + changedBitsStack[i] = null; + currentValueStack[i] = null; + stack[i] = null; + } + index$1 = -1; + nextRoot = null; + nextRenderExpirationTime = 0; + nextUnitOfWork = null; + isRootReadyForCommit = !1; + } + function isAlreadyFailedLegacyErrorBoundary(instance) { + return ( + null !== legacyErrorBoundariesThatAlreadyFailed && + legacyErrorBoundariesThatAlreadyFailed.has(instance) + ); + } function completeUnitOfWork(workInProgress$jscomp$0) { for (;;) { - var next = completeWork( - workInProgress$jscomp$0.alternate, - workInProgress$jscomp$0, - nextRenderExpirationTime - ), + var current = workInProgress$jscomp$0.alternate, returnFiber = workInProgress$jscomp$0["return"], siblingFiber = workInProgress$jscomp$0.sibling; - var workInProgress = workInProgress$jscomp$0; - if ( - 1073741823 === nextRenderExpirationTime || - 1073741823 !== workInProgress.expirationTime - ) { - if (2 !== workInProgress.tag && 3 !== workInProgress.tag) - var newExpirationTime = 0; - else - (newExpirationTime = workInProgress.updateQueue), - (newExpirationTime = - null === newExpirationTime - ? 0 - : newExpirationTime.expirationTime); - for (var child = workInProgress.child; null !== child; ) - 0 !== child.expirationTime && - (0 === newExpirationTime || - newExpirationTime > child.expirationTime) && - (newExpirationTime = child.expirationTime), - (child = child.sibling); - workInProgress.expirationTime = newExpirationTime; - } - if (null !== next) return next; - null !== returnFiber && - (null === returnFiber.firstEffect && - (returnFiber.firstEffect = workInProgress$jscomp$0.firstEffect), - null !== workInProgress$jscomp$0.lastEffect && - (null !== returnFiber.lastEffect && - (returnFiber.lastEffect.nextEffect = - workInProgress$jscomp$0.firstEffect), - (returnFiber.lastEffect = workInProgress$jscomp$0.lastEffect)), - 1 < workInProgress$jscomp$0.effectTag && - (null !== returnFiber.lastEffect - ? (returnFiber.lastEffect.nextEffect = workInProgress$jscomp$0) - : (returnFiber.firstEffect = workInProgress$jscomp$0), - (returnFiber.lastEffect = workInProgress$jscomp$0))); - if (null !== siblingFiber) return siblingFiber; - if (null !== returnFiber) workInProgress$jscomp$0 = returnFiber; - else { - workInProgress$jscomp$0.stateNode.isReadyForCommit = !0; - break; + if (0 === (workInProgress$jscomp$0.effectTag & 512)) { + current = completeWork( + current, + workInProgress$jscomp$0, + nextRenderExpirationTime + ); + var workInProgress = workInProgress$jscomp$0; + if ( + 1073741823 === nextRenderExpirationTime || + 1073741823 !== workInProgress.expirationTime + ) { + b: switch (workInProgress.tag) { + case 3: + case 2: + var newExpirationTime = workInProgress.updateQueue; + newExpirationTime = + null === newExpirationTime + ? 0 + : newExpirationTime.expirationTime; + break b; + default: + newExpirationTime = 0; + } + for (var child = workInProgress.child; null !== child; ) + 0 !== child.expirationTime && + (0 === newExpirationTime || + newExpirationTime > child.expirationTime) && + (newExpirationTime = child.expirationTime), + (child = child.sibling); + workInProgress.expirationTime = newExpirationTime; + } + if (null !== current) return current; + null !== returnFiber && + 0 === (returnFiber.effectTag & 512) && + (null === returnFiber.firstEffect && + (returnFiber.firstEffect = workInProgress$jscomp$0.firstEffect), + null !== workInProgress$jscomp$0.lastEffect && + (null !== returnFiber.lastEffect && + (returnFiber.lastEffect.nextEffect = + workInProgress$jscomp$0.firstEffect), + (returnFiber.lastEffect = workInProgress$jscomp$0.lastEffect)), + 1 < workInProgress$jscomp$0.effectTag && + (null !== returnFiber.lastEffect + ? (returnFiber.lastEffect.nextEffect = workInProgress$jscomp$0) + : (returnFiber.firstEffect = workInProgress$jscomp$0), + (returnFiber.lastEffect = workInProgress$jscomp$0))); + if (null !== siblingFiber) return siblingFiber; + if (null !== returnFiber) workInProgress$jscomp$0 = returnFiber; + else { + isRootReadyForCommit = !0; + break; + } + } else { + workInProgress$jscomp$0 = unwindWork(workInProgress$jscomp$0); + if (null !== workInProgress$jscomp$0) + return ( + (workInProgress$jscomp$0.effectTag &= 511), workInProgress$jscomp$0 + ); + null !== returnFiber && + ((returnFiber.firstEffect = returnFiber.lastEffect = null), + (returnFiber.effectTag |= 512)); + if (null !== siblingFiber) return siblingFiber; + if (null !== returnFiber) workInProgress$jscomp$0 = returnFiber; + else break; } } return null; @@ -4522,247 +4682,114 @@ function ReactFiberScheduler(config) { ReactCurrentOwner.current = null; return next; } - function performFailedUnitOfWork(workInProgress) { - var next = beginFailedWork( - workInProgress.alternate, - workInProgress, - nextRenderExpirationTime - ); - null === next && (next = completeUnitOfWork(workInProgress)); - ReactCurrentOwner.current = null; - return next; - } - function workLoop(isAsync) { - if (null !== capturedErrors) - if (isAsync) - for (; null !== nextUnitOfWork && !shouldYield(); ) - nextUnitOfWork = hasCapturedError(nextUnitOfWork) - ? performFailedUnitOfWork(nextUnitOfWork) - : performUnitOfWork(nextUnitOfWork); - else - for (; null !== nextUnitOfWork; ) - nextUnitOfWork = hasCapturedError(nextUnitOfWork) - ? performFailedUnitOfWork(nextUnitOfWork) - : performUnitOfWork(nextUnitOfWork); - else if (isAsync) - for (; null !== nextUnitOfWork && !shouldYield(); ) - nextUnitOfWork = performUnitOfWork(nextUnitOfWork); - else - for (; null !== nextUnitOfWork; ) - nextUnitOfWork = performUnitOfWork(nextUnitOfWork); - } function renderRoot(root, expirationTime, isAsync) { invariant( !isWorking, "renderRoot was called recursively. This error is likely caused by a bug in React. Please file an issue." ); isWorking = !0; - root.isReadyForCommit = !1; if ( - root !== nextRoot || expirationTime !== nextRenderExpirationTime || + root !== nextRoot || null === nextUnitOfWork - ) { - for (; -1 < index; ) (valueStack[index] = null), index--; - 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; - changedBitsStack[i] = null; - currentValueStack[i] = null; - stack[i] = null; - } - index$1 = -1; - resetHostContainer(); - nextRoot = root; - nextRenderExpirationTime = expirationTime; - nextUnitOfWork = createWorkInProgress( - nextRoot.current, - null, - expirationTime - ); - } - i = !1; - context = null; - try { - workLoop(isAsync); - } catch (e) { - (i = !0), (context = e); - } - for (; i; ) { - if (didFatal) { - firstUncaughtError = context; - break; - } - expirationTime = nextUnitOfWork; - if (null === expirationTime) didFatal = !0; - else { - var boundary = captureError(expirationTime, context); - 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 { - i = boundary; - context = isAsync; - for (boundary = i; null !== expirationTime; ) { - switch (expirationTime.tag) { - case 2: - popContextProvider(expirationTime); - break; - case 5: - popHostContext(expirationTime); - break; - case 3: - popHostContainer(expirationTime); - break; - case 4: - popHostContainer(expirationTime); - break; - case 13: - popProvider(expirationTime); - } - if ( - expirationTime === boundary || - expirationTime.alternate === boundary - ) - break; - expirationTime = expirationTime["return"]; - } - nextUnitOfWork = performFailedUnitOfWork(i); - workLoop(context); - } catch (e) { - i = !0; - context = e; - continue; - } + ) + resetContextStack(), + (nextRoot = root), + (nextRenderExpirationTime = expirationTime), + (nextUnitOfWork = createWorkInProgress( + nextRoot.current, + null, + nextRenderExpirationTime + )), + (root.pendingCommitExpirationTime = 0); + var didFatal = !1; + do { + try { + if (isAsync) + for (; null !== nextUnitOfWork && !shouldYield(); ) + nextUnitOfWork = performUnitOfWork(nextUnitOfWork); + else + for (; null !== nextUnitOfWork; ) + nextUnitOfWork = performUnitOfWork(nextUnitOfWork); + } catch (thrownValue) { + if (null === nextUnitOfWork) { + didFatal = !0; + onUncaughtError(thrownValue); break; } - } - } - isAsync = firstUncaughtError; - didFatal = isWorking = !1; - firstUncaughtError = null; - null !== isAsync && onUncaughtError(isAsync); - return root.isReadyForCommit ? root.current.alternate : null; - } - function captureError(failedWork, error$jscomp$0) { - var boundary = (ReactCurrentOwner.current = null), - errorBoundaryFound = !1, - willRetry = !1, - errorBoundaryName = null; - if (3 === failedWork.tag) - (boundary = failedWork), isFailedBoundary(failedWork) && (didFatal = !0); - else - for ( - var node = failedWork["return"]; - null !== node && null === boundary; - - ) { - 2 === node.tag - ? "function" === typeof node.stateNode.componentDidCatch && - ((errorBoundaryFound = !0), - (errorBoundaryName = getComponentName(node)), - (boundary = node), - (willRetry = !0)) - : 3 === node.tag && (boundary = node); - if (isFailedBoundary(node)) { - if ( - isUnmounting || - (null !== commitPhaseBoundaries && - (commitPhaseBoundaries.has(node) || - (null !== node.alternate && - commitPhaseBoundaries.has(node.alternate)))) - ) - return null; - boundary = null; - willRetry = !1; + isAsync = nextUnitOfWork; + var returnFiber = isAsync["return"]; + if (null === returnFiber) { + didFatal = !0; + onUncaughtError(thrownValue); + break; } - node = node["return"]; + throwException(returnFiber, isAsync, thrownValue); + nextUnitOfWork = completeUnitOfWork(isAsync); } - if (null !== boundary) { - null === failedBoundaries && (failedBoundaries = new Set()); - failedBoundaries.add(boundary); - var info = ""; - node = failedWork; - do { - a: switch (node.tag) { - case 0: - case 1: + break; + } while (1); + isWorking = !1; + if (didFatal || null !== nextUnitOfWork) return null; + if (isRootReadyForCommit) + return ( + (root.pendingCommitExpirationTime = expirationTime), + root.current.alternate + ); + invariant( + !1, + "Expired work should have completed. This error is likely caused by a bug in React. Please file an issue." + ); + } + function scheduleCapture(sourceFiber, boundaryFiber, value, expirationTime) { + sourceFiber = { + value: value, + source: sourceFiber, + stack: getStackAddendumByWorkInProgressFiber(sourceFiber) + }; + insertUpdateIntoFiber(boundaryFiber, { + expirationTime: expirationTime, + partialState: null, + callback: null, + isReplace: !1, + isForced: !1, + capturedValue: sourceFiber, + next: null + }); + scheduleWork(boundaryFiber, expirationTime); + } + function onCommitPhaseError(fiber$jscomp$0, error) { + a: { + invariant( + !isWorking || isCommitting, + "dispatch: Cannot dispatch during the render phase." + ); + for (var fiber = fiber$jscomp$0["return"]; null !== fiber; ) { + switch (fiber.tag) { case 2: - case 5: - var owner = node._debugOwner, - source = node._debugSource; - var JSCompiler_inline_result = getComponentName(node); - var ownerName = null; - owner && (ownerName = getComponentName(owner)); - owner = source; - JSCompiler_inline_result = - "\n in " + - (JSCompiler_inline_result || "Unknown") + - (owner - ? " (at " + - owner.fileName.replace(/^.*[\\\/]/, "") + - ":" + - owner.lineNumber + - ")" - : ownerName ? " (created by " + ownerName + ")" : ""); + var instance = fiber.stateNode; + if ( + "function" === typeof fiber.type.getDerivedStateFromCatch || + ("function" === typeof instance.componentDidCatch && + !isAlreadyFailedLegacyErrorBoundary(instance)) + ) { + scheduleCapture(fiber$jscomp$0, fiber, error, 1); + fiber$jscomp$0 = void 0; + break a; + } + break; + case 3: + scheduleCapture(fiber$jscomp$0, fiber, error, 1); + fiber$jscomp$0 = void 0; break a; - default: - JSCompiler_inline_result = ""; } - info += JSCompiler_inline_result; - node = node["return"]; - } while (node); - node = info; - failedWork = getComponentName(failedWork); - null === capturedErrors && (capturedErrors = new Map()); - error$jscomp$0 = { - componentName: failedWork, - componentStack: node, - error: error$jscomp$0, - errorBoundary: errorBoundaryFound ? boundary.stateNode : null, - errorBoundaryFound: errorBoundaryFound, - errorBoundaryName: errorBoundaryName, - willRetry: willRetry - }; - capturedErrors.set(boundary, error$jscomp$0); - try { - var error = error$jscomp$0.error; - (error && error.suppressReactErrorLogging) || console.error(error); - } catch (e) { - (e && e.suppressReactErrorLogging) || console.error(e); + fiber = fiber["return"]; } - isCommitting - ? (null === commitPhaseBoundaries && - (commitPhaseBoundaries = new Set()), - commitPhaseBoundaries.add(boundary)) - : scheduleErrorRecovery(boundary); - return boundary; + 3 === fiber$jscomp$0.tag && + scheduleCapture(fiber$jscomp$0, fiber$jscomp$0, error, 1); + fiber$jscomp$0 = void 0; } - null === firstUncaughtError && (firstUncaughtError = error$jscomp$0); - return null; - } - function hasCapturedError(fiber) { - return ( - null !== capturedErrors && - (capturedErrors.has(fiber) || - (null !== fiber.alternate && capturedErrors.has(fiber.alternate))) - ); - } - function isFailedBoundary(fiber) { - return ( - null !== failedBoundaries && - (failedBoundaries.has(fiber) || - (null !== fiber.alternate && failedBoundaries.has(fiber.alternate))) - ); - } - function computeAsyncExpiration() { - return 20 * ((((recalculateCurrentTime() + 100) / 20) | 0) + 1); + return fiber$jscomp$0; } function computeExpirationForFiber(fiber) { fiber = @@ -4773,7 +4800,7 @@ function ReactFiberScheduler(config) { : fiber.mode & 1 ? isBatchingInteractiveUpdates ? 10 * ((((recalculateCurrentTime() + 50) / 10) | 0) + 1) - : computeAsyncExpiration() + : 25 * ((((recalculateCurrentTime() + 500) / 25) | 0) + 1) : 1; isBatchingInteractiveUpdates && (0 === lowestPendingInteractiveExpirationTime || @@ -4782,39 +4809,41 @@ function ReactFiberScheduler(config) { return fiber; } function scheduleWork(fiber, expirationTime) { - return scheduleWorkImpl(fiber, expirationTime, !1); - } - function scheduleWorkImpl(fiber, expirationTime) { - for (; null !== fiber; ) { - if (0 === fiber.expirationTime || fiber.expirationTime > expirationTime) - fiber.expirationTime = expirationTime; - null !== fiber.alternate && - (0 === fiber.alternate.expirationTime || - fiber.alternate.expirationTime > expirationTime) && - (fiber.alternate.expirationTime = expirationTime); - if (null === fiber["return"]) - if (3 === fiber.tag) { - var root = fiber.stateNode; - !isWorking && - root === nextRoot && - expirationTime < nextRenderExpirationTime && - ((nextUnitOfWork = nextRoot = null), - (nextRenderExpirationTime = 0)); - requestWork(root, expirationTime); - !isWorking && - root === nextRoot && - expirationTime < nextRenderExpirationTime && - ((nextUnitOfWork = nextRoot = null), - (nextRenderExpirationTime = 0)); - } else break; - fiber = fiber["return"]; + a: { + for (; null !== fiber; ) { + if (0 === fiber.expirationTime || fiber.expirationTime > expirationTime) + fiber.expirationTime = expirationTime; + null !== fiber.alternate && + (0 === fiber.alternate.expirationTime || + fiber.alternate.expirationTime > expirationTime) && + (fiber.alternate.expirationTime = expirationTime); + if (null === fiber["return"]) + if (3 === fiber.tag) { + var root = fiber.stateNode; + !isWorking && + 0 !== nextRenderExpirationTime && + expirationTime < nextRenderExpirationTime && + resetContextStack(); + (nextRoot === root && isWorking) || + requestWork(root, expirationTime); + nestedUpdateCount > NESTED_UPDATE_LIMIT && + invariant( + !1, + "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." + ); + } else { + expirationTime = void 0; + break a; + } + fiber = fiber["return"]; + } + expirationTime = void 0; } - } - function scheduleErrorRecovery(fiber) { - scheduleWorkImpl(fiber, 1, !0); + return expirationTime; } function recalculateCurrentTime() { - return (mostRecentCurrentTime = (((now() - startTime) / 10) | 0) + 2); + mostRecentCurrentTimeMs = now() - originalStartTimeMs; + return (mostRecentCurrentTime = ((mostRecentCurrentTimeMs / 10) | 0) + 2); } function syncUpdates(fn, a, b, c, d) { var previousExpirationContext = expirationContext; @@ -4830,18 +4859,13 @@ function ReactFiberScheduler(config) { if (expirationTime > callbackExpirationTime) return; cancelDeferredCallback(callbackID); } - var currentMs = now() - startTime; + var currentMs = now() - originalStartTimeMs; callbackExpirationTime = expirationTime; callbackID = scheduleDeferredCallback(performAsyncWork, { timeout: 10 * (expirationTime - 2) - currentMs }); } function requestWork(root, expirationTime) { - nestedUpdateCount > NESTED_UPDATE_LIMIT && - invariant( - !1, - "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." - ); if (null === root.nextScheduledRoot) (root.remainingExpirationTime = expirationTime), null === lastScheduledRoot @@ -4914,7 +4938,8 @@ function ReactFiberScheduler(config) { } previousScheduledRoot = nextFlushedRoot; null !== previousScheduledRoot && - previousScheduledRoot === highestPriorityRoot + previousScheduledRoot === highestPriorityRoot && + 1 === highestPriorityWork ? nestedUpdateCount++ : (nestedUpdateCount = 0); nextFlushedRoot = highestPriorityRoot; @@ -4959,10 +4984,10 @@ function ReactFiberScheduler(config) { scheduleCallbackWithExpiration(nextFlushedExpirationTime); deadline = null; deadlineDidExpire = !1; - nestedUpdateCount = 0; finishRendering(); } function finishRendering() { + nestedUpdateCount = 0; if (null !== completedBatches) { var batches = completedBatches; completedBatches = null; @@ -5027,18 +5052,24 @@ function ReactFiberScheduler(config) { expirationTime.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." ); - expirationTime.isReadyForCommit = !1; + firstBatch = expirationTime.pendingCommitExpirationTime; + invariant( + 0 !== firstBatch, + "Cannot commit an incomplete root. This error is likely caused by a bug in React. Please file an issue." + ); + expirationTime.pendingCommitExpirationTime = 0; + var currentTime = recalculateCurrentTime(); ReactCurrentOwner.current = null; - 1 < finishedWork.effectTag - ? null !== finishedWork.lastEffect - ? ((finishedWork.lastEffect.nextEffect = finishedWork), - (firstBatch = finishedWork.firstEffect)) - : (firstBatch = finishedWork) - : (firstBatch = finishedWork.firstEffect); + if (1 < finishedWork.effectTag) + if (null !== finishedWork.lastEffect) { + finishedWork.lastEffect.nextEffect = finishedWork; + var firstEffect = finishedWork.firstEffect; + } else firstEffect = finishedWork; + else firstEffect = finishedWork.firstEffect; prepareForCommit(expirationTime.containerInfo); - for (nextEffect = firstBatch; null !== nextEffect; ) { + for (nextEffect = firstEffect; null !== nextEffect; ) { var didError = !1, - _error = void 0; + error = void 0; try { for (; null !== nextEffect; ) { var effectTag = nextEffect.effectTag; @@ -5047,7 +5078,7 @@ function ReactFiberScheduler(config) { var current = nextEffect.alternate; null !== current && commitDetachRef(current); } - switch (effectTag & -242) { + switch (effectTag & 14) { case 2: commitPlacement(nextEffect); nextEffect.effectTag &= -3; @@ -5061,65 +5092,46 @@ function ReactFiberScheduler(config) { commitWork(nextEffect.alternate, nextEffect); break; case 8: - (isUnmounting = !0), - commitDeletion(nextEffect), - (isUnmounting = !1); + commitDeletion(nextEffect); } nextEffect = nextEffect.nextEffect; } } catch (e) { - (didError = !0), (_error = e); + (didError = !0), (error = e); } didError && (invariant( null !== nextEffect, "Should have next effect. This error is likely caused by a bug in React. Please file an issue." ), - captureError(nextEffect, _error), + onCommitPhaseError(nextEffect, error), null !== nextEffect && (nextEffect = nextEffect.nextEffect)); } resetAfterCommit(expirationTime.containerInfo); expirationTime.current = finishedWork; - for (nextEffect = firstBatch; null !== nextEffect; ) { + for (nextEffect = firstEffect; null !== nextEffect; ) { effectTag = !1; current = void 0; try { - for (; null !== nextEffect; ) { + for ( + firstEffect = expirationTime, + didError = currentTime, + error = firstBatch; + null !== nextEffect; + + ) { var effectTag$jscomp$0 = nextEffect.effectTag; effectTag$jscomp$0 & 36 && - commitLifeCycles(nextEffect.alternate, nextEffect); + commitLifeCycles( + firstEffect, + nextEffect.alternate, + nextEffect, + didError, + error + ); + effectTag$jscomp$0 & 256 && + commitErrorLogging(nextEffect, onUncaughtError); effectTag$jscomp$0 & 128 && commitAttachRef(nextEffect); - if (effectTag$jscomp$0 & 64) - switch (((firstBatch = nextEffect), - (didError = void 0), - null !== capturedErrors && - ((didError = capturedErrors.get(firstBatch)), - capturedErrors["delete"](firstBatch), - null == didError && - null !== firstBatch.alternate && - ((firstBatch = firstBatch.alternate), - (didError = capturedErrors.get(firstBatch)), - capturedErrors["delete"](firstBatch))), - invariant( - null != didError, - "No error for given unit of work. This error is likely caused by a bug in React. Please file an issue." - ), - firstBatch.tag)) { - case 2: - firstBatch.stateNode.componentDidCatch(didError.error, { - componentStack: didError.componentStack - }); - break; - case 3: - null === firstUncaughtError && - (firstUncaughtError = didError.error); - break; - default: - invariant( - !1, - "Invalid type of work. This error is likely caused by a bug in React. Please file an issue." - ); - } var next = nextEffect.nextEffect; nextEffect.nextEffect = null; nextEffect = next; @@ -5132,20 +5144,13 @@ function ReactFiberScheduler(config) { null !== nextEffect, "Should have next effect. This error is likely caused by a bug in React. Please file an issue." ), - captureError(nextEffect, current), + onCommitPhaseError(nextEffect, current), null !== nextEffect && (nextEffect = nextEffect.nextEffect)); } isWorking = isCommitting = !1; "function" === typeof onCommitRoot && onCommitRoot(finishedWork.stateNode); - commitPhaseBoundaries && - (commitPhaseBoundaries.forEach(scheduleErrorRecovery), - (commitPhaseBoundaries = null)); - null !== firstUncaughtError && - ((finishedWork = firstUncaughtError), - (firstUncaughtError = null), - onUncaughtError(finishedWork)); finishedWork = expirationTime.current.expirationTime; - 0 === finishedWork && (failedBoundaries = capturedErrors = null); + 0 === finishedWork && (legacyErrorBoundariesThatAlreadyFailed = null); root.remainingExpirationTime = finishedWork; } function shouldYield() { @@ -5164,26 +5169,41 @@ function ReactFiberScheduler(config) { } var hostContext = ReactFiberHostContext(config), hydrationContext = ReactFiberHydrationContext(config), - popHostContainer = hostContext.popHostContainer, - popHostContext = hostContext.popHostContext, resetHostContainer = hostContext.resetHostContainer, - _ReactFiberBeginWork = ReactFiberBeginWork( + beginWork = ReactFiberBeginWork( config, hostContext, hydrationContext, scheduleWork, computeExpirationForFiber - ), - beginWork = _ReactFiberBeginWork.beginWork, - beginFailedWork = _ReactFiberBeginWork.beginFailedWork, + ).beginWork, completeWork = ReactFiberCompleteWork(config, hostContext, hydrationContext) .completeWork; - hostContext = ReactFiberCommitWork(config, captureError); + hostContext = ReactFiberUnwindWork( + hostContext, + scheduleWork, + isAlreadyFailedLegacyErrorBoundary + ); + var throwException = hostContext.throwException, + unwindWork = hostContext.unwindWork; + hostContext = ReactFiberCommitWork( + config, + onCommitPhaseError, + scheduleWork, + computeExpirationForFiber, + function(instance) { + null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([instance])) + : legacyErrorBoundariesThatAlreadyFailed.add(instance); + }, + recalculateCurrentTime + ); var commitResetTextContent = hostContext.commitResetTextContent, commitPlacement = hostContext.commitPlacement, commitDeletion = hostContext.commitDeletion, commitWork = hostContext.commitWork, commitLifeCycles = hostContext.commitLifeCycles, + commitErrorLogging = hostContext.commitErrorLogging, commitAttachRef = hostContext.commitAttachRef, commitDetachRef = hostContext.commitDetachRef, now = config.now, @@ -5191,8 +5211,9 @@ function ReactFiberScheduler(config) { cancelDeferredCallback = config.cancelDeferredCallback, prepareForCommit = config.prepareForCommit, resetAfterCommit = config.resetAfterCommit, - startTime = now(), + originalStartTimeMs = now(), mostRecentCurrentTime = 2, + mostRecentCurrentTimeMs = originalStartTimeMs, lastUniqueAsyncExpiration = 0, expirationContext = 0, isWorking = !1, @@ -5200,13 +5221,9 @@ function ReactFiberScheduler(config) { nextRoot = null, nextRenderExpirationTime = 0, nextEffect = null, - capturedErrors = null, - failedBoundaries = null, - commitPhaseBoundaries = null, - firstUncaughtError = null, - didFatal = !1, isCommitting = !1, - isUnmounting = !1, + isRootReadyForCommit = !1, + legacyErrorBoundariesThatAlreadyFailed = null, firstScheduledRoot = null, lastScheduledRoot = null, callbackExpirationTime = 0, @@ -5227,6 +5244,7 @@ function ReactFiberScheduler(config) { nestedUpdateCount = 0, timeHeuristicForUnitOfWork = 1; return { + recalculateCurrentTime: recalculateCurrentTime, computeExpirationForFiber: computeExpirationForFiber, scheduleWork: scheduleWork, requestWork: requestWork, @@ -5287,7 +5305,8 @@ function ReactFiberScheduler(config) { }, deferredUpdates: function(fn) { var previousExpirationContext = expirationContext; - expirationContext = computeAsyncExpiration(); + expirationContext = + 25 * ((((recalculateCurrentTime() + 500) / 25) | 0) + 1); try { return fn(); } finally { @@ -5321,7 +5340,7 @@ function ReactFiberScheduler(config) { (lowestPendingInteractiveExpirationTime = 0)); }, computeUniqueAsyncExpiration: function() { - var result = computeAsyncExpiration(); + var result = 25 * ((((recalculateCurrentTime() + 500) / 25) | 0) + 1); result <= lastUniqueAsyncExpiration && (result = lastUniqueAsyncExpiration + 1); return (lastUniqueAsyncExpiration = result); @@ -5333,10 +5352,11 @@ function ReactFiberReconciler$1(config) { element, container, parentComponent, + currentTime, expirationTime, callback ) { - var current = container.current; + currentTime = container.current; if (parentComponent) { parentComponent = parentComponent._reactInternalFiber; var parentContext; @@ -5368,15 +5388,16 @@ function ReactFiberReconciler$1(config) { ? (container.context = parentComponent) : (container.pendingContext = parentComponent); container = callback; - insertUpdateIntoFiber(current, { + insertUpdateIntoFiber(currentTime, { expirationTime: expirationTime, partialState: { element: element }, callback: void 0 === container ? null : container, isReplace: !1, isForced: !1, + capturedValue: null, next: null }); - scheduleWork(current, expirationTime); + scheduleWork(currentTime, expirationTime); return expirationTime; } function findHostInstance(fiber) { @@ -5385,7 +5406,8 @@ function ReactFiberReconciler$1(config) { } var getPublicInstance = config.getPublicInstance; config = ReactFiberScheduler(config); - var computeExpirationForFiber = config.computeExpirationForFiber, + var recalculateCurrentTime = config.recalculateCurrentTime, + computeExpirationForFiber = config.computeExpirationForFiber, scheduleWork = config.scheduleWork; return { createContainer: function(containerInfo, isAsync, hydrate) { @@ -5394,28 +5416,47 @@ function ReactFiberReconciler$1(config) { current: isAsync, containerInfo: containerInfo, pendingChildren: null, - remainingExpirationTime: 0, - isReadyForCommit: !1, + pendingCommitExpirationTime: 0, finishedWork: null, context: null, pendingContext: null, hydrate: hydrate, + remainingExpirationTime: 0, firstBatch: null, nextScheduledRoot: null }; return (isAsync.stateNode = containerInfo); }, updateContainer: function(element, container, parentComponent, callback) { - var expirationTime = computeExpirationForFiber(container.current); + var current = container.current, + currentTime = recalculateCurrentTime(); + current = computeExpirationForFiber(current); return updateContainerAtExpirationTime( element, container, parentComponent, + currentTime, + current, + callback + ); + }, + updateContainerAtExpirationTime: function( + element, + container, + parentComponent, + expirationTime, + callback + ) { + var currentTime = recalculateCurrentTime(); + return updateContainerAtExpirationTime( + element, + container, + parentComponent, + currentTime, expirationTime, callback ); }, - updateContainerAtExpirationTime: updateContainerAtExpirationTime, flushRoot: config.flushRoot, requestWork: config.requestWork, computeUniqueAsyncExpiration: config.computeUniqueAsyncExpiration, @@ -5466,6 +5507,27 @@ var ReactFiberReconciler$2 = Object.freeze({ default: ReactFiberReconciler$1 }), reactReconciler = ReactFiberReconciler$3["default"] ? ReactFiberReconciler$3["default"] : ReactFiberReconciler$3, + ReactNativeTagHandles = { + tagsStartAt: 1, + tagCount: 1, + allocateTag: function() { + for (; this.reactTagIsNativeTopRootID(ReactNativeTagHandles.tagCount); ) + ReactNativeTagHandles.tagCount++; + var tag = ReactNativeTagHandles.tagCount; + ReactNativeTagHandles.tagCount++; + return tag; + }, + assertRootTag: function(tag) { + invariant( + this.reactTagIsNativeTopRootID(tag), + "Expect a native root tag, instead got %s", + tag + ); + }, + reactTagIsNativeTopRootID: function(reactTag) { + return 1 === reactTag % 10; + } + }, ReactFabricHostComponent = (function() { function ReactFabricHostComponent(tag, viewConfig, props) { if (!(this instanceof ReactFabricHostComponent)) @@ -5686,7 +5748,28 @@ var roots = new Map(), 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null ); }, - unstable_batchedUpdates: batchedUpdates, + unstable_batchedUpdates: function(fn, bookkeeping) { + if (isBatching) return fn(bookkeeping); + isBatching = !0; + try { + return _batchedUpdates(fn, bookkeeping); + } finally { + if ( + ((isBatching = !1), null !== restoreTarget || null !== restoreQueue) + ) + if ( + (_flushInteractiveUpdates(), + restoreTarget && + ((bookkeeping = restoreTarget), + (fn = restoreQueue), + (restoreQueue = restoreTarget = null), + restoreStateOfTarget(bookkeeping), + fn)) + ) + for (bookkeeping = 0; bookkeeping < fn.length; bookkeeping++) + restoreStateOfTarget(fn[bookkeeping]); + } + }, flushSync: ReactFabricRenderer.flushSync, __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: { NativeMethodsMixin: { @@ -5739,7 +5822,6 @@ var roots = new Map(), } }, ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin, - ReactGlobalSharedState: ReactGlobalSharedState, ReactNativeComponentTree: ReactNativeComponentTree, ReactNativePropRegistry: ReactNativePropRegistry, TouchHistoryMath: TouchHistoryMath, @@ -5764,7 +5846,7 @@ ReactFabricRenderer.injectIntoDevTools({ findFiberByHostInstance: getInstanceFromTag, getInspectorDataForViewTag: getInspectorDataForViewTag, bundleType: 0, - version: "16.3.0-alpha.0", + version: "16.3.0-alpha.1", rendererPackageName: "react-native-renderer" }); var ReactFabric$2 = Object.freeze({ default: ReactFabric }), diff --git a/Libraries/Renderer/ReactNativeRenderer-dev.js b/Libraries/Renderer/ReactNativeRenderer-dev.js index e562e89af04..fc98f17e176 100644 --- a/Libraries/Renderer/ReactNativeRenderer-dev.js +++ b/Libraries/Renderer/ReactNativeRenderer-dev.js @@ -19,12 +19,12 @@ require("InitializeCore"); var invariant = require("fbjs/lib/invariant"); var warning = require("fbjs/lib/warning"); var emptyFunction = require("fbjs/lib/emptyFunction"); -var RCTEventEmitter = require("RCTEventEmitter"); var UIManager = require("UIManager"); -var React = require("react"); +var RCTEventEmitter = require("RCTEventEmitter"); var TextInputState = require("TextInputState"); var deepDiffer = require("deepDiffer"); var flattenStyle = require("flattenStyle"); +var React = require("react"); var emptyObject = require("fbjs/lib/emptyObject"); var checkPropTypes = require("prop-types/checkPropTypes"); var shallowEqual = require("fbjs/lib/shallowEqual"); @@ -2323,6 +2323,10 @@ var ReactNativeBridgeEventPlugin = { nativeEvent, nativeEventTarget ) { + if (targetInst == null) { + // Probably a node belonging to another renderer's tree. + return null; + } var bubbleDispatchConfig = customBubblingEventTypes[topLevelType]; var directDispatchConfig = customDirectEventTypes[topLevelType]; invariant( @@ -2434,6 +2438,49 @@ var ReactNativeComponentTree = Object.freeze({ updateFiberProps: updateFiberProps }); +var ReactNativeEventPluginOrder = [ + "ResponderEventPlugin", + "ReactNativeBridgeEventPlugin" +]; + +// Module provided by RN: +var ReactNativeGlobalResponderHandler = { + onChange: function(from, to, blockNativeResponder) { + if (to !== null) { + var tag = to.stateNode._nativeTag; + UIManager.setJSResponder(tag, blockNativeResponder); + } else { + UIManager.clearJSResponder(); + } + } +}; + +/** + * Make sure essential globals are available and are patched correctly. Please don't remove this + * line. Bundles created by react-packager `require` it before executing any application code. This + * ensures it exists in the dependency graph and can be `require`d. + * TODO: require this in packager, not in React #10932517 + */ +// Module provided by RN: +/** + * Inject module for resolving DOM hierarchy and plugin ordering. + */ +injection.injectEventPluginOrder(ReactNativeEventPluginOrder); +injection$1.injectComponentTree(ReactNativeComponentTree); + +ResponderEventPlugin.injection.injectGlobalResponderHandler( + ReactNativeGlobalResponderHandler +); + +/** + * Some important event plugins included by default (without having to require + * them). + */ +injection.injectEventPluginsByName({ + ResponderEventPlugin: ResponderEventPlugin, + ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin +}); + // Use to restore controlled state after a change event has fired. var fiberHostComponent = null; @@ -2732,55 +2779,12 @@ var ReactNativeEventEmitter = Object.freeze({ receiveTouches: receiveTouches }); -var ReactNativeEventPluginOrder = [ - "ResponderEventPlugin", - "ReactNativeBridgeEventPlugin" -]; - -// Module provided by RN: -var ReactNativeGlobalResponderHandler = { - onChange: function(from, to, blockNativeResponder) { - if (to !== null) { - var tag = to.stateNode._nativeTag; - UIManager.setJSResponder(tag, blockNativeResponder); - } else { - UIManager.clearJSResponder(); - } - } -}; - -/** - * Make sure essential globals are available and are patched correctly. Please don't remove this - * line. Bundles created by react-packager `require` it before executing any application code. This - * ensures it exists in the dependency graph and can be `require`d. - * TODO: require this in packager, not in React #10932517 - */ -// Module provided by RN: // Module provided by RN: /** * Register the event emitter with the native bridge */ RCTEventEmitter.register(ReactNativeEventEmitter); -/** - * Inject module for resolving DOM hierarchy and plugin ordering. - */ -injection.injectEventPluginOrder(ReactNativeEventPluginOrder); -injection$1.injectComponentTree(ReactNativeComponentTree); - -ResponderEventPlugin.injection.injectGlobalResponderHandler( - ReactNativeGlobalResponderHandler -); - -/** - * Some important event plugins included by default (without having to require - * them). - */ -injection.injectEventPluginsByName({ - ResponderEventPlugin: ResponderEventPlugin, - ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin -}); - // 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"]; @@ -2977,19 +2981,9 @@ var TouchHistoryMath = { noCentroid: -1 }; -var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; - -var ReactCurrentOwner = ReactInternals.ReactCurrentOwner; -var ReactDebugCurrentFrame = ReactInternals.ReactDebugCurrentFrame; - -var ReactGlobalSharedState = Object.freeze({ - ReactCurrentOwner: ReactCurrentOwner, - ReactDebugCurrentFrame: ReactDebugCurrentFrame -}); - // TODO: this is special because it gets imported during build. -var ReactVersion = "16.3.0-alpha.0"; +var ReactVersion = "16.3.0-alpha.1"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { @@ -3580,6 +3574,11 @@ function set(key, value) { key._reactInternalFiber = value; } +var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + +var ReactCurrentOwner = ReactInternals.ReactCurrentOwner; +var ReactDebugCurrentFrame = ReactInternals.ReactDebugCurrentFrame; + function getComponentName(fiber) { var type = fiber.type; @@ -4098,19 +4097,26 @@ var ReactNativeComponent = (function(_React$Component) { return ReactNativeComponent; })(React.Component); -// Don't change these two values: -var NoEffect = 0; -var PerformedWork = 1; +// Don't change these two values. They're used by React Dev Tools. +var NoEffect = /* */ 0; +var PerformedWork = /* */ 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 Placement = /* */ 2; +var Update = /* */ 4; +var PlacementAndUpdate = /* */ 6; +var Deletion = /* */ 8; +var ContentReset = /* */ 16; +var Callback = /* */ 32; +var DidCapture = /* */ 64; +var Ref = /* */ 128; +var ErrLog = /* */ 256; + +// Union of all host effects +var HostEffectMask = /* */ 511; + +var Incomplete = /* */ 512; +var ShouldCapture = /* */ 1024; var MOUNTING = 1; var MOUNTED = 2; @@ -4542,10 +4548,13 @@ var ReactDebugCurrentFiber = { // Re-export dynamic flags from the fbsource version. var _require = require("ReactFeatureFlags"); +var enableGetDerivedStateFromCatch = _require.enableGetDerivedStateFromCatch; var debugRenderPhaseSideEffects = _require.debugRenderPhaseSideEffects; var debugRenderPhaseSideEffectsForStrictMode = _require.debugRenderPhaseSideEffectsForStrictMode; var warnAboutDeprecatedLifecycles = _require.warnAboutDeprecatedLifecycles; +var replayFailedUnitOfWorkWithInvokeGuardedCallback = + _require.replayFailedUnitOfWorkWithInvokeGuardedCallback; var enableUserTimingAPI = true; var enableMutatingReconciler = true; @@ -4682,6 +4691,8 @@ var shouldIgnoreFiber = function(fiber) { case CallComponent: case ReturnComponent: case Fragment: + case ContextProvider: + case ContextConsumer: return true; default: return false; @@ -5591,12 +5602,12 @@ function createFiberRoot(containerInfo, isAsync, hydrate) { current: uninitializedFiber, containerInfo: containerInfo, pendingChildren: null, - remainingExpirationTime: NoWork, - isReadyForCommit: false, + pendingCommitExpirationTime: NoWork, finishedWork: null, context: null, pendingContext: null, hydrate: hydrate, + remainingExpirationTime: NoWork, firstBatch: null, nextScheduledRoot: null }; @@ -6038,7 +6049,8 @@ function createUpdateQueue(baseState) { last: null, callbackList: null, hasForceUpdate: false, - isInitialized: false + isInitialized: false, + capturedValues: null }; { queue.isProcessing = false; @@ -6063,7 +6075,10 @@ function insertUpdateIntoQueue(queue, update) { } } -function insertUpdateIntoFiber(fiber, update) { +var q1 = void 0; +var q2 = void 0; +function ensureUpdateQueues(fiber) { + q1 = q2 = null; // We'll have at least one and at most two distinct update queues. var alternateFiber = fiber.alternate; var queue1 = fiber.updateQueue; @@ -6086,6 +6101,16 @@ function insertUpdateIntoFiber(fiber, update) { } queue2 = queue2 !== queue1 ? queue2 : null; + // Use module variables instead of returning a tuple + q1 = queue1; + q2 = queue2; +} + +function insertUpdateIntoFiber(fiber, update) { + ensureUpdateQueues(fiber); + var queue1 = q1; + var queue2 = q2; + // Warn if an update is scheduled from inside an updater function. { if ( @@ -6125,14 +6150,17 @@ function insertUpdateIntoFiber(fiber, update) { } function getUpdateExpirationTime(fiber) { - if (fiber.tag !== ClassComponent && fiber.tag !== HostRoot) { - return NoWork; + switch (fiber.tag) { + case HostRoot: + case ClassComponent: + var updateQueue = fiber.updateQueue; + if (updateQueue === null) { + return NoWork; + } + return updateQueue.expirationTime; + default: + return NoWork; } - var updateQueue = fiber.updateQueue; - if (updateQueue === null) { - return NoWork; - } - return updateQueue.expirationTime; } function getStateFromUpdate(update, instance, prevState, props) { @@ -6161,6 +6189,7 @@ function processUpdateQueue( first: currentQueue.first, last: currentQueue.last, isInitialized: currentQueue.isInitialized, + capturedValues: currentQueue.capturedValues, // These fields are no longer valid because they were already committed. // Reset them. callbackList: null, @@ -6262,12 +6291,24 @@ function processUpdateQueue( } _callbackList.push(update); } + if (update.capturedValue !== null) { + var _capturedValues = queue.capturedValues; + if (_capturedValues === null) { + queue.capturedValues = [update.capturedValue]; + } else { + _capturedValues.push(update.capturedValue); + } + } update = update.next; } if (queue.callbackList !== null) { workInProgress.effectTag |= Callback; - } else if (queue.first === null && !queue.hasForceUpdate) { + } else if ( + queue.first === null && + !queue.hasForceUpdate && + queue.capturedValues === null + ) { // The queue is empty. We can reset it. workInProgress.updateQueue = null; } @@ -6363,6 +6404,18 @@ var warnOnInvalidCallback = void 0; }); Object.freeze(fakeInternalInstance); } +function callGetDerivedStateFromCatch(ctor, capturedValues) { + var resultState = {}; + for (var i = 0; i < capturedValues.length; i++) { + var capturedValue = capturedValues[i]; + var error = capturedValue.value; + var partialState = ctor.getDerivedStateFromCatch.call(null, error); + if (partialState !== null && partialState !== undefined) { + Object.assign(resultState, partialState); + } + } + return resultState; +} var ReactFiberClassComponent = function( scheduleWork, @@ -6386,7 +6439,7 @@ var ReactFiberClassComponent = function( callback: callback, isReplace: false, isForced: false, - nextCallback: null, + capturedValue: null, next: null }; insertUpdateIntoFiber(fiber, update); @@ -6405,7 +6458,7 @@ var ReactFiberClassComponent = function( callback: callback, isReplace: true, isForced: false, - nextCallback: null, + capturedValue: null, next: null }; insertUpdateIntoFiber(fiber, update); @@ -6424,7 +6477,7 @@ var ReactFiberClassComponent = function( callback: callback, isReplace: false, isForced: true, - nextCallback: null, + capturedValue: null, next: null }; insertUpdateIntoFiber(fiber, update); @@ -6450,7 +6503,7 @@ var ReactFiberClassComponent = function( } var instance = workInProgress.stateNode; - var type = workInProgress.type; + var ctor = workInProgress.type; if (typeof instance.shouldComponentUpdate === "function") { startPhaseTimer(workInProgress, "shouldComponentUpdate"); var shouldUpdate = instance.shouldComponentUpdate( @@ -6472,7 +6525,7 @@ var ReactFiberClassComponent = function( return shouldUpdate; } - if (type.prototype && type.prototype.isPureReactComponent) { + if (ctor.prototype && ctor.prototype.isPureReactComponent) { return ( !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) ); @@ -6628,7 +6681,7 @@ var ReactFiberClassComponent = function( } if (typeof instance.getChildContext === "function") { warning( - typeof workInProgress.type.childContextTypes === "object", + typeof type.childContextTypes === "object", "%s.getChildContext(): childContextTypes must be defined in order to " + "use getChildContext().", getComponentName(workInProgress) @@ -6849,6 +6902,7 @@ var ReactFiberClassComponent = function( // Invokes the mount life-cycles on a previously never rendered instance. function mountClassInstance(workInProgress, renderExpirationTime) { + var ctor = workInProgress.type; var current = workInProgress.alternate; { @@ -6885,7 +6939,7 @@ var ReactFiberClassComponent = function( if ( (typeof instance.UNSAFE_componentWillMount === "function" || typeof instance.componentWillMount === "function") && - typeof workInProgress.type.getDerivedStateFromProps !== "function" + typeof ctor.getDerivedStateFromProps !== "function" ) { callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's @@ -6907,113 +6961,8 @@ var ReactFiberClassComponent = function( } } - // Called on a preexisting class instance. Returns false if a resumed render - // could be reused. - // function resumeMountClassInstance( - // workInProgress: Fiber, - // priorityLevel: PriorityLevel, - // ): boolean { - // const instance = workInProgress.stateNode; - // resetInputPointers(workInProgress, instance); - - // let newState = workInProgress.memoizedState; - // let newProps = workInProgress.pendingProps; - // if (!newProps) { - // // If there isn't any new props, then we'll reuse the memoized props. - // // This could be from already completed work. - // newProps = workInProgress.memoizedProps; - // invariant( - // newProps != null, - // 'There should always be pending or memoized props. This error is ' + - // 'likely caused by a bug in React. Please file an issue.', - // ); - // } - // const newUnmaskedContext = getUnmaskedContext(workInProgress); - // const newContext = getMaskedContext(workInProgress, newUnmaskedContext); - - // const oldContext = instance.context; - // const oldProps = workInProgress.memoizedProps; - - // if ( - // typeof instance.componentWillReceiveProps === 'function' && - // (oldProps !== newProps || oldContext !== newContext) - // ) { - // callComponentWillReceiveProps( - // workInProgress, - // instance, - // newProps, - // newContext, - // ); - // } - - // // Process the update queue before calling shouldComponentUpdate - // const updateQueue = workInProgress.updateQueue; - // if (updateQueue !== null) { - // newState = processUpdateQueue( - // workInProgress, - // updateQueue, - // instance, - // newState, - // newProps, - // priorityLevel, - // ); - // } - - // // TODO: Should we deal with a setState that happened after the last - // // componentWillMount and before this componentWillMount? Probably - // // unsupported anyway. - - // if ( - // !checkShouldComponentUpdate( - // workInProgress, - // workInProgress.memoizedProps, - // newProps, - // workInProgress.memoizedState, - // newState, - // newContext, - // ) - // ) { - // // Update the existing instance's state, props, and context pointers even - // // though we're bailing out. - // instance.props = newProps; - // instance.state = newState; - // instance.context = newContext; - // return false; - // } - - // // Update the input pointers now so that they are correct when we call - // // componentWillMount - // instance.props = newProps; - // instance.state = newState; - // instance.context = newContext; - - // if (typeof instance.componentWillMount === 'function') { - // callComponentWillMount(workInProgress, instance); - // // componentWillMount may have called setState. Process the update queue. - // const newUpdateQueue = workInProgress.updateQueue; - // if (newUpdateQueue !== null) { - // newState = processUpdateQueue( - // workInProgress, - // newUpdateQueue, - // instance, - // newState, - // newProps, - // priorityLevel, - // ); - // } - // } - - // if (typeof instance.componentDidMount === 'function') { - // workInProgress.effectTag |= Update; - // } - - // instance.state = newState; - - // return true; - // } - - // Invokes the update life-cycles and returns false if it shouldn't rerender. - function updateClassInstance(current, workInProgress, renderExpirationTime) { + function resumeMountClassInstance(workInProgress, renderExpirationTime) { + var ctor = workInProgress.type; var instance = workInProgress.stateNode; resetInputPointers(workInProgress, instance); @@ -7032,7 +6981,7 @@ var ReactFiberClassComponent = function( if ( (typeof instance.UNSAFE_componentWillReceiveProps === "function" || typeof instance.componentWillReceiveProps === "function") && - typeof workInProgress.type.getDerivedStateFromProps !== "function" + typeof ctor.getDerivedStateFromProps !== "function" ) { if (oldProps !== newProps || oldContext !== newContext) { callComponentWillReceiveProps( @@ -7044,9 +6993,9 @@ var ReactFiberClassComponent = function( } } - var partialState = void 0; + var derivedStateFromProps = void 0; if (oldProps !== newProps) { - partialState = callGetDerivedStateFromProps( + derivedStateFromProps = callGetDerivedStateFromProps( workInProgress, instance, newProps @@ -7057,6 +7006,172 @@ var ReactFiberClassComponent = function( var oldState = workInProgress.memoizedState; // TODO: Previous state can be null. var newState = void 0; + var derivedStateFromCatch = void 0; + if (workInProgress.updateQueue !== null) { + newState = processUpdateQueue( + null, + workInProgress, + workInProgress.updateQueue, + instance, + newProps, + renderExpirationTime + ); + + var updateQueue = workInProgress.updateQueue; + if ( + updateQueue !== null && + updateQueue.capturedValues !== null && + enableGetDerivedStateFromCatch && + typeof ctor.getDerivedStateFromCatch === "function" + ) { + var capturedValues = updateQueue.capturedValues; + // Don't remove these from the update queue yet. We need them in + // finishClassComponent. Do the reset there. + // TODO: This is awkward. Refactor class components. + // updateQueue.capturedValues = null; + derivedStateFromCatch = callGetDerivedStateFromCatch( + ctor, + capturedValues + ); + } + } else { + newState = oldState; + } + + if (derivedStateFromProps !== null && derivedStateFromProps !== 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 + ? derivedStateFromProps + : Object.assign({}, newState, derivedStateFromProps); + } + if (derivedStateFromCatch !== null && derivedStateFromCatch !== 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 + ? derivedStateFromCatch + : Object.assign({}, newState, derivedStateFromCatch); + } + + 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.componentDidMount === "function") { + workInProgress.effectTag |= Update; + } + return false; + } + + var shouldUpdate = checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldState, + newState, + newContext + ); + + 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 ctor.getDerivedStateFromProps !== "function" + ) { + startPhaseTimer(workInProgress, "componentWillUpdate"); + if (typeof instance.componentWillUpdate === "function") { + instance.componentWillUpdate(newProps, newState, newContext); + } + if (typeof instance.UNSAFE_componentWillUpdate === "function") { + 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.componentDidMount === "function") { + workInProgress.effectTag |= Update; + } + + // If shouldComponentUpdate returned false, we should still update the + // memoized props/state to indicate that this work can be reused. + memoizeProps(workInProgress, newProps); + memoizeState(workInProgress, newState); + } + + // Update the existing instance's state, props, and context pointers even + // if shouldComponentUpdate returns false. + instance.props = newProps; + instance.state = newState; + instance.context = newContext; + + return shouldUpdate; + } + + // Invokes the update life-cycles and returns false if it shouldn't rerender. + function updateClassInstance(current, workInProgress, renderExpirationTime) { + var ctor = workInProgress.type; + var instance = workInProgress.stateNode; + resetInputPointers(workInProgress, instance); + + var oldProps = workInProgress.memoizedProps; + var newProps = workInProgress.pendingProps; + var oldContext = instance.context; + var newUnmaskedContext = getUnmaskedContext(workInProgress); + var newContext = getMaskedContext(workInProgress, newUnmaskedContext); + + // Note: During these life-cycles, instance.props/instance.state are what + // ever the previously attempted to render - not the "current". However, + // during componentDidUpdate we pass the "current" props. + + // 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 ctor.getDerivedStateFromProps !== "function" + ) { + if (oldProps !== newProps || oldContext !== newContext) { + callComponentWillReceiveProps( + workInProgress, + instance, + newProps, + newContext + ); + } + } + + var derivedStateFromProps = void 0; + if (oldProps !== newProps) { + derivedStateFromProps = callGetDerivedStateFromProps( + workInProgress, + instance, + newProps + ); + } + + // 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; + var derivedStateFromCatch = void 0; if (workInProgress.updateQueue !== null) { newState = processUpdateQueue( current, @@ -7066,18 +7181,45 @@ var ReactFiberClassComponent = function( newProps, renderExpirationTime ); + + var updateQueue = workInProgress.updateQueue; + if ( + updateQueue !== null && + updateQueue.capturedValues !== null && + enableGetDerivedStateFromCatch && + typeof ctor.getDerivedStateFromCatch === "function" + ) { + var capturedValues = updateQueue.capturedValues; + // Don't remove these from the update queue yet. We need them in + // finishClassComponent. Do the reset there. + // TODO: This is awkward. Refactor class components. + // updateQueue.capturedValues = null; + derivedStateFromCatch = callGetDerivedStateFromCatch( + ctor, + capturedValues + ); + } } else { newState = oldState; } - if (partialState !== null && partialState !== undefined) { + if (derivedStateFromProps !== null && derivedStateFromProps !== 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); + ? derivedStateFromProps + : Object.assign({}, newState, derivedStateFromProps); + } + if (derivedStateFromCatch !== null && derivedStateFromCatch !== 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 + ? derivedStateFromCatch + : Object.assign({}, newState, derivedStateFromCatch); } if ( @@ -7117,7 +7259,7 @@ var ReactFiberClassComponent = function( if ( (typeof instance.UNSAFE_componentWillUpdate === "function" || typeof instance.componentWillUpdate === "function") && - typeof workInProgress.type.getDerivedStateFromProps !== "function" + typeof ctor.getDerivedStateFromProps !== "function" ) { startPhaseTimer(workInProgress, "componentWillUpdate"); if (typeof instance.componentWillUpdate === "function") { @@ -7163,7 +7305,7 @@ var ReactFiberClassComponent = function( callGetDerivedStateFromProps: callGetDerivedStateFromProps, constructClassInstance: constructClassInstance, mountClassInstance: mountClassInstance, - // resumeMountClassInstance, + resumeMountClassInstance: resumeMountClassInstance, updateClassInstance: updateClassInstance }; }; @@ -8531,6 +8673,7 @@ var ReactFiberBeginWork = function( _ReactFiberClassCompo.callGetDerivedStateFromProps, constructClassInstance = _ReactFiberClassCompo.constructClassInstance, mountClassInstance = _ReactFiberClassCompo.mountClassInstance, + resumeMountClassInstance = _ReactFiberClassCompo.resumeMountClassInstance, updateClassInstance = _ReactFiberClassCompo.updateClassInstance; // TODO: Remove this and use reconcileChildrenAtExpirationTime directly. @@ -8655,19 +8798,20 @@ var ReactFiberBeginWork = function( // 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) { + if (workInProgress.stateNode === null) { // 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); + shouldUpdate = resumeMountClassInstance( + workInProgress, + renderExpirationTime + ); } } else { shouldUpdate = updateClassInstance( @@ -8676,11 +8820,23 @@ var ReactFiberBeginWork = function( renderExpirationTime ); } + + // We processed the update queue inside updateClassInstance. It may have + // included some errors that were dispatched during the commit phase. + // TODO: Refactor class components so this is less awkward. + var didCaptureError = false; + var updateQueue = workInProgress.updateQueue; + if (updateQueue !== null && updateQueue.capturedValues !== null) { + shouldUpdate = true; + didCaptureError = true; + } return finishClassComponent( current, workInProgress, shouldUpdate, - hasContext + hasContext, + didCaptureError, + renderExpirationTime ); } @@ -8688,12 +8844,14 @@ var ReactFiberBeginWork = function( current, workInProgress, shouldUpdate, - hasContext + hasContext, + didCaptureError, + renderExpirationTime ) { // Refs should update even if shouldComponentUpdate returns false markRef(current, workInProgress); - if (!shouldUpdate) { + if (!shouldUpdate && !didCaptureError) { // Context providers should defer to sCU for rendering if (hasContext) { invalidateContextProvider(workInProgress, false); @@ -8702,26 +8860,60 @@ var ReactFiberBeginWork = function( return bailoutOnAlreadyFinishedWork(current, workInProgress); } + var ctor = workInProgress.type; var instance = workInProgress.stateNode; // Rerender ReactCurrentOwner.current = workInProgress; var nextChildren = void 0; - { - ReactDebugCurrentFiber.setCurrentPhase("render"); - nextChildren = instance.render(); - if ( - debugRenderPhaseSideEffects || - (debugRenderPhaseSideEffectsForStrictMode && - workInProgress.mode & StrictMode) - ) { - instance.render(); + if ( + didCaptureError && + (!enableGetDerivedStateFromCatch || + typeof ctor.getDerivedStateFromCatch !== "function") + ) { + // If we captured an error, but getDerivedStateFrom catch is not defined, + // unmount all the children. componentDidCatch will schedule an update to + // re-render a fallback. This is temporary until we migrate everyone to + // the new API. + // TODO: Warn in a future release. + nextChildren = null; + } else { + { + ReactDebugCurrentFiber.setCurrentPhase("render"); + nextChildren = instance.render(); + if ( + debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.mode & StrictMode) + ) { + instance.render(); + } + ReactDebugCurrentFiber.setCurrentPhase(null); } - ReactDebugCurrentFiber.setCurrentPhase(null); } + // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; - reconcileChildren(current, workInProgress, nextChildren); + if (didCaptureError) { + // If we're recovering from an error, reconcile twice: first to delete + // all the existing children. + reconcileChildrenAtExpirationTime( + current, + workInProgress, + null, + renderExpirationTime + ); + workInProgress.child = null; + // Now we can continue reconciling like normal. This has the effect of + // remounting all children regardless of whether their their + // identity matches. + } + reconcileChildrenAtExpirationTime( + current, + workInProgress, + nextChildren, + renderExpirationTime + ); // 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); @@ -8763,13 +8955,21 @@ var ReactFiberBeginWork = function( null, renderExpirationTime ); - if (prevState === state) { + memoizeState(workInProgress, state); + updateQueue = workInProgress.updateQueue; + + var element = void 0; + if (updateQueue !== null && updateQueue.capturedValues !== null) { + // There's an uncaught error. Unmount the whole root. + element = null; + } else 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); + } else { + element = state.element; } - var element = state.element; var root = workInProgress.stateNode; if ( (current === null || current.child === null) && @@ -8826,7 +9026,18 @@ var ReactFiberBeginWork = function( // 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 isHidden = + workInProgress.mode & AsyncMode && + shouldDeprioritizeSubtree(type, nextProps); + if (isHidden) { + // Before bailing out, make sure we've deprioritized a hidden component. + workInProgress.expirationTime = Never; + } + if (!isHidden || renderExpirationTime !== Never) { + return bailoutOnAlreadyFinishedWork(current, workInProgress); + } + // If we're rendering a hidden node at hidden priority, don't bailout. The + // parent is complete, but the children may not be. } var nextChildren = nextProps.children; @@ -8855,6 +9066,7 @@ var ReactFiberBeginWork = function( // Down-prioritize the children. workInProgress.expirationTime = Never; // Bailout and come back to this fiber later. + workInProgress.memoizedProps = nextProps; return null; } @@ -8948,7 +9160,14 @@ var ReactFiberBeginWork = function( var hasContext = pushContextProvider(workInProgress); adoptClassInstance(workInProgress, value); mountClassInstance(workInProgress, renderExpirationTime); - return finishClassComponent(current, workInProgress, true, hasContext); + return finishClassComponent( + current, + workInProgress, + true, + hasContext, + false, + renderExpirationTime + ); } else { // Proceed under the assumption that this is a functional component workInProgress.tag = FunctionalComponent; @@ -9276,6 +9495,16 @@ var ReactFiberBeginWork = function( workInProgress.stateNode = observedBits; var render = newProps.children; + + if (typeof render !== "function") { + invariant( + false, + "A context consumer was rendered with multiple children, or a child that isn't a function. " + + "A context consumer expects a single child that is a function. " + + "If you did pass a function, make sure there is no trailing or leading whitespace around it." + ); + } + var newChildren = render(newValue); reconcileChildren(current, workInProgress, newChildren); return workInProgress.child; @@ -9437,70 +9666,8 @@ var ReactFiberBeginWork = function( } } - function beginFailedWork(current, workInProgress, renderExpirationTime) { - // Push context providers here to avoid a push/pop context mismatch. - switch (workInProgress.tag) { - case ClassComponent: - pushContextProvider(workInProgress); - break; - case HostRoot: - pushHostRootContext(workInProgress); - break; - default: - invariant( - false, - "Invalid type of work. This error is likely caused by a bug in React. " + - "Please file an issue." - ); - } - - // Add an error effect so we can handle the error during the commit phase - workInProgress.effectTag |= Err; - - // This is a weird case where we do "resume" work — work that failed on - // our first attempt. Because we no longer have a notion of "progressed - // deletions," reset the child to the current child to make sure we delete - // it again. TODO: Find a better way to handle this, perhaps during a more - // general overhaul of error handling. - if (current === null) { - workInProgress.child = null; - } else if (workInProgress.child !== current.child) { - workInProgress.child = current.child; - } - - if ( - workInProgress.expirationTime === NoWork || - workInProgress.expirationTime > renderExpirationTime - ) { - return bailoutOnLowPriority(current, workInProgress); - } - - // If we don't bail out, we're going be recomputing our children so we need - // to drop our effect list. - workInProgress.firstEffect = null; - workInProgress.lastEffect = null; - - // Unmount the current children as if the component rendered null - var nextChildren = null; - reconcileChildrenAtExpirationTime( - current, - workInProgress, - nextChildren, - renderExpirationTime - ); - - if (workInProgress.tag === ClassComponent) { - var instance = workInProgress.stateNode; - workInProgress.memoizedProps = instance.props; - workInProgress.memoizedState = instance.state; - } - - return workInProgress.child; - } - return { - beginWork: beginWork, - beginFailedWork: beginFailedWork + beginWork: beginWork }; }; @@ -9718,14 +9885,12 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { } 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); + portalOrRoot.pendingChildren = newChildSet; // Schedule an update on the container to swap out the container. markUpdate(workInProgress); + finalizeContainerChildren(container, newChildSet); } }; updateHostComponent = function( @@ -9834,6 +9999,20 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { case ClassComponent: { // We are leaving this subtree, so pop context if any. popContextProvider(workInProgress); + + // If this component caught an error, schedule an error log effect. + var instance = workInProgress.stateNode; + var updateQueue = workInProgress.updateQueue; + if (updateQueue !== null && updateQueue.capturedValues !== null) { + workInProgress.effectTag &= ~DidCapture; + if (typeof instance.componentDidCatch === "function") { + workInProgress.effectTag |= ErrLog; + } else { + // Normally we clear this in the commit phase, but since we did not + // schedule an effect, we need to reset it here. + updateQueue.capturedValues = null; + } + } return null; } case HostRoot: { @@ -9844,7 +10023,6 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { fiberRoot.context = fiberRoot.pendingContext; fiberRoot.pendingContext = null; } - if (current === null || current.child === null) { // If we hydrated, pop so that we can delete any remaining children // that weren't hydrated. @@ -9854,6 +10032,11 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { workInProgress.effectTag &= ~Placement; } updateHostContainer(workInProgress); + + var _updateQueue = workInProgress.updateQueue; + if (_updateQueue !== null && _updateQueue.capturedValues !== null) { + workInProgress.effectTag |= ErrLog; + } return null; } case HostComponent: { @@ -9868,10 +10051,13 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { // 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 _instance = workInProgress.stateNode; var currentHostContext = getHostContext(); + // TODO: Experiencing an error where oldProps is null. Suggests a host + // component is hitting the resume path. Figure out why. Possibly + // related to `hidden`. var updatePayload = prepareUpdate( - instance, + _instance, type, oldProps, newProps, @@ -9925,7 +10111,7 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { markUpdate(workInProgress); } } else { - var _instance = createInstance( + var _instance2 = createInstance( type, newProps, rootContainerInstance, @@ -9933,14 +10119,14 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { workInProgress ); - appendAllChildren(_instance, workInProgress); + appendAllChildren(_instance2, 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, + _instance2, type, newProps, rootContainerInstance, @@ -9949,7 +10135,7 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { ) { markUpdate(workInProgress); } - workInProgress.stateNode = _instance; + workInProgress.stateNode = _instance2; } if (workInProgress.ref !== null) { @@ -10044,11 +10230,267 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) { }; }; +function createCapturedValue(value, source) { + // If the value is an error, call this function immediately after it is thrown + // so the stack is accurate. + return { + value: value, + source: source, + stack: getStackAddendumByWorkInProgressFiber(source) + }; +} + +var ReactFiberUnwindWork = function( + hostContext, + scheduleWork, + isAlreadyFailedLegacyErrorBoundary +) { + var popHostContainer = hostContext.popHostContainer, + popHostContext = hostContext.popHostContext; + + function throwException(returnFiber, sourceFiber, rawValue) { + // The source fiber did not complete. + sourceFiber.effectTag |= Incomplete; + // Its effect list is no longer valid. + sourceFiber.firstEffect = sourceFiber.lastEffect = null; + + var value = createCapturedValue(rawValue, sourceFiber); + + var workInProgress = returnFiber; + do { + switch (workInProgress.tag) { + case HostRoot: { + // Uncaught error + var errorInfo = value; + ensureUpdateQueues(workInProgress); + var updateQueue = workInProgress.updateQueue; + updateQueue.capturedValues = [errorInfo]; + workInProgress.effectTag |= ShouldCapture; + return; + } + case ClassComponent: + // Capture and retry + var ctor = workInProgress.type; + var _instance = workInProgress.stateNode; + if ( + (workInProgress.effectTag & DidCapture) === NoEffect && + ((typeof ctor.getDerivedStateFromCatch === "function" && + enableGetDerivedStateFromCatch) || + (_instance !== null && + typeof _instance.componentDidCatch === "function" && + !isAlreadyFailedLegacyErrorBoundary(_instance))) + ) { + ensureUpdateQueues(workInProgress); + var _updateQueue = workInProgress.updateQueue; + var capturedValues = _updateQueue.capturedValues; + if (capturedValues === null) { + _updateQueue.capturedValues = [value]; + } else { + capturedValues.push(value); + } + workInProgress.effectTag |= ShouldCapture; + return; + } + break; + default: + break; + } + workInProgress = workInProgress["return"]; + } while (workInProgress !== null); + } + + function unwindWork(workInProgress) { + switch (workInProgress.tag) { + case ClassComponent: { + popContextProvider(workInProgress); + var effectTag = workInProgress.effectTag; + if (effectTag & ShouldCapture) { + workInProgress.effectTag = (effectTag & ~ShouldCapture) | DidCapture; + return workInProgress; + } + return null; + } + case HostRoot: { + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + var _effectTag = workInProgress.effectTag; + if (_effectTag & ShouldCapture) { + workInProgress.effectTag = (_effectTag & ~ShouldCapture) | DidCapture; + return workInProgress; + } + return null; + } + case HostComponent: { + popHostContext(workInProgress); + return null; + } + case HostPortal: + popHostContainer(workInProgress); + return null; + case ContextProvider: + popProvider(workInProgress); + return null; + default: + return null; + } + } + return { + throwException: throwException, + unwindWork: unwindWork + }; +}; + +// 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; + + 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 logCapturedError(capturedError) { + var logError = showErrorDialog(capturedError); + + // Allow injected showErrorDialog() to prevent default console.error logging. + // This enables renderers like ReactNative to better manage redbox behavior. + if (logError === false) { + return; + } + + var error = capturedError.error; + var suppressLogging = error && error.suppressReactErrorLogging; + if (suppressLogging) { + return; + } + + { + var componentName = capturedError.componentName, + componentStack = capturedError.componentStack, + errorBoundaryName = capturedError.errorBoundaryName, + errorBoundaryFound = capturedError.errorBoundaryFound, + willRetry = capturedError.willRetry; + + var componentNameMessage = componentName + ? "The above error occurred in the <" + componentName + "> component:" + : "The above error occurred in one of your React components:"; + + var errorBoundaryMessage = void 0; + // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow. + if (errorBoundaryFound && errorBoundaryName) { + if (willRetry) { + errorBoundaryMessage = + "React will try to recreate this component tree from scratch " + + ("using the error boundary you provided, " + errorBoundaryName + "."); + } else { + errorBoundaryMessage = + "This error was initially handled by the error boundary " + + errorBoundaryName + + ".\n" + + "Recreating the tree from scratch failed so React will unmount the tree."; + } + } else { + errorBoundaryMessage = + "Consider adding an error boundary to your tree to customize error handling behavior.\n" + + "Visit https://fb.me/react-error-boundaries to learn more about error boundaries."; + } + var combinedMessage = + "" + + componentNameMessage + + componentStack + + "\n\n" + + ("" + errorBoundaryMessage); + + // In development, we provide our own message with just the component stack. + // We don't include the original error message and JS stack because the browser + // has already printed it. Even if the application swallows the error, it is still + // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils. + console.error(combinedMessage); + } +} + var invokeGuardedCallback$3 = ReactErrorUtils.invokeGuardedCallback; var hasCaughtError$1 = ReactErrorUtils.hasCaughtError; var clearCaughtError$1 = ReactErrorUtils.clearCaughtError; -var ReactFiberCommitWork = function(config, captureError) { +function logError(boundary, errorInfo) { + var source = errorInfo.source; + var stack = errorInfo.stack; + if (stack === null) { + stack = getStackAddendumByWorkInProgressFiber(source); + } + + var capturedError = { + componentName: source !== null ? getComponentName(source) : null, + error: errorInfo.value, + errorBoundary: boundary, + componentStack: stack !== null ? stack : "", + errorBoundaryName: null, + errorBoundaryFound: false, + willRetry: false + }; + + if (boundary !== null) { + capturedError.errorBoundaryName = getComponentName(boundary); + capturedError.errorBoundaryFound = capturedError.willRetry = + boundary.tag === ClassComponent; + } else { + capturedError.errorBoundaryName = null; + capturedError.errorBoundaryFound = capturedError.willRetry = false; + } + + 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); + } + } +} + +var ReactFiberCommitWork = function( + config, + captureError, + scheduleWork, + computeExpirationForFiber, + markLegacyErrorBoundaryAsFailed, + recalculateCurrentTime +) { var getPublicInstance = config.getPublicInstance, mutation = config.mutation, persistence = config.persistence; @@ -10095,53 +10537,59 @@ var ReactFiberCommitWork = function(config, captureError) { } } - function commitLifeCycles(current, finishedWork) { + function commitLifeCycles( + finishedRoot, + current, + finishedWork, + currentTime, + committedExpirationTime + ) { switch (finishedWork.tag) { case ClassComponent: { - var instance = finishedWork.stateNode; + var _instance = finishedWork.stateNode; if (finishedWork.effectTag & Update) { if (current === null) { startPhaseTimer(finishedWork, "componentDidMount"); - instance.props = finishedWork.memoizedProps; - instance.state = finishedWork.memoizedState; - instance.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); + _instance.props = finishedWork.memoizedProps; + _instance.state = finishedWork.memoizedState; + _instance.componentDidUpdate(prevProps, prevState); stopPhaseTimer(); } } var updateQueue = finishedWork.updateQueue; if (updateQueue !== null) { - commitCallbacks(updateQueue, instance); + commitCallbacks(updateQueue, _instance); } return; } case HostRoot: { var _updateQueue = finishedWork.updateQueue; if (_updateQueue !== null) { - var _instance = null; + var _instance2 = null; if (finishedWork.child !== null) { switch (finishedWork.child.tag) { case HostComponent: - _instance = getPublicInstance(finishedWork.child.stateNode); + _instance2 = getPublicInstance(finishedWork.child.stateNode); break; case ClassComponent: - _instance = finishedWork.child.stateNode; + _instance2 = finishedWork.child.stateNode; break; } } - commitCallbacks(_updateQueue, _instance); + commitCallbacks(_updateQueue, _instance2); } return; } case HostComponent: { - var _instance2 = finishedWork.stateNode; + var _instance3 = 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). @@ -10150,7 +10598,7 @@ var ReactFiberCommitWork = function(config, captureError) { if (current === null && finishedWork.effectTag & Update) { var type = finishedWork.type; var props = finishedWork.memoizedProps; - commitMount(_instance2, type, props, finishedWork); + commitMount(_instance3, type, props, finishedWork); } return; @@ -10173,17 +10621,78 @@ var ReactFiberCommitWork = function(config, captureError) { } } + function commitErrorLogging(finishedWork, onUncaughtError) { + switch (finishedWork.tag) { + case ClassComponent: + { + var ctor = finishedWork.type; + var _instance4 = finishedWork.stateNode; + var updateQueue = finishedWork.updateQueue; + invariant( + updateQueue !== null && updateQueue.capturedValues !== null, + "An error logging effect should not have been scheduled if no errors " + + "were captured. This error is likely caused by a bug in React. " + + "Please file an issue." + ); + var capturedErrors = updateQueue.capturedValues; + updateQueue.capturedValues = null; + + if (typeof ctor.getDerivedStateFromCatch !== "function") { + // To preserve the preexisting retry behavior of error boundaries, + // we keep track of which ones already failed during this batch. + // This gets reset before we yield back to the browser. + // TODO: Warn in strict mode if getDerivedStateFromCatch is + // not defined. + markLegacyErrorBoundaryAsFailed(_instance4); + } + + _instance4.props = finishedWork.memoizedProps; + _instance4.state = finishedWork.memoizedState; + for (var i = 0; i < capturedErrors.length; i++) { + var errorInfo = capturedErrors[i]; + var _error = errorInfo.value; + logError(finishedWork, errorInfo); + _instance4.componentDidCatch(_error); + } + } + break; + case HostRoot: { + var _updateQueue2 = finishedWork.updateQueue; + invariant( + _updateQueue2 !== null && _updateQueue2.capturedValues !== null, + "An error logging effect should not have been scheduled if no errors " + + "were captured. This error is likely caused by a bug in React. " + + "Please file an issue." + ); + var _capturedErrors = _updateQueue2.capturedValues; + _updateQueue2.capturedValues = null; + for (var _i = 0; _i < _capturedErrors.length; _i++) { + var _errorInfo = _capturedErrors[_i]; + logError(finishedWork, _errorInfo); + onUncaughtError(_errorInfo.value); + } + break; + } + default: + invariant( + false, + "This unit of work tag cannot capture errors. This error is " + + "likely caused by a bug in React. Please file an issue." + ); + } + } + function commitAttachRef(finishedWork) { var ref = finishedWork.ref; if (ref !== null) { - var instance = finishedWork.stateNode; + var _instance5 = finishedWork.stateNode; var instanceToUse = void 0; switch (finishedWork.tag) { case HostComponent: - instanceToUse = getPublicInstance(instance); + instanceToUse = getPublicInstance(_instance5); break; default: - instanceToUse = instance; + instanceToUse = _instance5; } if (typeof ref === "function") { ref(instanceToUse); @@ -10215,9 +10724,9 @@ var ReactFiberCommitWork = function(config, captureError) { switch (current.tag) { case ClassComponent: { safelyDetachRef(current); - var instance = current.stateNode; - if (typeof instance.componentWillUnmount === "function") { - safelyCallComponentWillUnmount(current, instance); + var _instance6 = current.stateNode; + if (typeof _instance6.componentWillUnmount === "function") { + safelyCallComponentWillUnmount(current, _instance6); } return; } @@ -10354,6 +10863,7 @@ var ReactFiberCommitWork = function(config, captureError) { }, commitLifeCycles: commitLifeCycles, + commitErrorLogging: commitErrorLogging, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; @@ -10611,8 +11121,8 @@ var ReactFiberCommitWork = function(config, captureError) { return; } case HostComponent: { - var instance = finishedWork.stateNode; - if (instance != null) { + var _instance7 = finishedWork.stateNode; + if (_instance7 != null) { // Commit the work prepared earlier. var newProps = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps @@ -10625,7 +11135,7 @@ var ReactFiberCommitWork = function(config, captureError) { finishedWork.updateQueue = null; if (updatePayload !== null) { commitUpdate( - instance, + _instance7, updatePayload, type, oldProps, @@ -10675,6 +11185,7 @@ var ReactFiberCommitWork = function(config, captureError) { commitDeletion: commitDeletion, commitWork: commitWork, commitLifeCycles: commitLifeCycles, + commitErrorLogging: commitErrorLogging, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; @@ -11140,107 +11651,6 @@ var ReactFiberInstrumentation = { var ReactFiberInstrumentation_1 = ReactFiberInstrumentation; -// 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; - - 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 logCapturedError(capturedError) { - var logError = showErrorDialog(capturedError); - - // Allow injected showErrorDialog() to prevent default console.error logging. - // This enables renderers like ReactNative to better manage redbox behavior. - if (logError === false) { - return; - } - - var error = capturedError.error; - var suppressLogging = error && error.suppressReactErrorLogging; - if (suppressLogging) { - return; - } - - { - var componentName = capturedError.componentName, - componentStack = capturedError.componentStack, - errorBoundaryName = capturedError.errorBoundaryName, - errorBoundaryFound = capturedError.errorBoundaryFound, - willRetry = capturedError.willRetry; - - var componentNameMessage = componentName - ? "The above error occurred in the <" + componentName + "> component:" - : "The above error occurred in one of your React components:"; - - var errorBoundaryMessage = void 0; - // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow. - if (errorBoundaryFound && errorBoundaryName) { - if (willRetry) { - errorBoundaryMessage = - "React will try to recreate this component tree from scratch " + - ("using the error boundary you provided, " + errorBoundaryName + "."); - } else { - errorBoundaryMessage = - "This error was initially handled by the error boundary " + - errorBoundaryName + - ".\n" + - "Recreating the tree from scratch failed so React will unmount the tree."; - } - } else { - errorBoundaryMessage = - "Consider adding an error boundary to your tree to customize error handling behavior.\n" + - "Visit https://fb.me/react-error-boundaries to learn more about error boundaries."; - } - var combinedMessage = - "" + - componentNameMessage + - componentStack + - "\n\n" + - ("" + errorBoundaryMessage); - - // In development, we provide our own message with just the component stack. - // We don't include the original error message and JS stack because the browser - // has already printed it. Even if the application swallows the error, it is still - // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils. - console.error(combinedMessage); - } -} - var invokeGuardedCallback$2 = ReactErrorUtils.invokeGuardedCallback; var hasCaughtError = ReactErrorUtils.hasCaughtError; var clearCaughtError = ReactErrorUtils.clearCaughtError; @@ -11302,10 +11712,11 @@ var warnAboutInvalidUpdates = void 0; var ReactFiberScheduler = function(config) { var hostContext = ReactFiberHostContext(config); + var popHostContext = hostContext.popHostContext, + popHostContainer = hostContext.popHostContainer; + var hydrationContext = ReactFiberHydrationContext(config); - var popHostContainer = hostContext.popHostContainer, - popHostContext = hostContext.popHostContext, - resetHostContainer = hostContext.resetHostContainer; + var resetHostContainer = hostContext.resetHostContainer; var _ReactFiberBeginWork = ReactFiberBeginWork( config, @@ -11314,8 +11725,7 @@ var ReactFiberScheduler = function(config) { scheduleWork, computeExpirationForFiber ), - beginWork = _ReactFiberBeginWork.beginWork, - beginFailedWork = _ReactFiberBeginWork.beginFailedWork; + beginWork = _ReactFiberBeginWork.beginWork; var _ReactFiberCompleteWo = ReactFiberCompleteWork( config, @@ -11324,12 +11734,28 @@ var ReactFiberScheduler = function(config) { ), completeWork = _ReactFiberCompleteWo.completeWork; - var _ReactFiberCommitWork = ReactFiberCommitWork(config, captureError), + var _ReactFiberUnwindWork = ReactFiberUnwindWork( + hostContext, + scheduleWork, + isAlreadyFailedLegacyErrorBoundary + ), + throwException = _ReactFiberUnwindWork.throwException, + unwindWork = _ReactFiberUnwindWork.unwindWork; + + var _ReactFiberCommitWork = ReactFiberCommitWork( + config, + onCommitPhaseError, + scheduleWork, + computeExpirationForFiber, + markLegacyErrorBoundaryAsFailed, + recalculateCurrentTime + ), commitResetTextContent = _ReactFiberCommitWork.commitResetTextContent, commitPlacement = _ReactFiberCommitWork.commitPlacement, commitDeletion = _ReactFiberCommitWork.commitDeletion, commitWork = _ReactFiberCommitWork.commitWork, commitLifeCycles = _ReactFiberCommitWork.commitLifeCycles, + commitErrorLogging = _ReactFiberCommitWork.commitErrorLogging, commitAttachRef = _ReactFiberCommitWork.commitAttachRef, commitDetachRef = _ReactFiberCommitWork.commitDetachRef; @@ -11341,8 +11767,9 @@ var ReactFiberScheduler = function(config) { // Represents the current time in ms. - var startTime = now(); + var originalStartTimeMs = now(); var mostRecentCurrentTime = msToExpirationTime(0); + var mostRecentCurrentTimeMs = originalStartTimeMs; // Used to ensure computeUniqueAsyncExpiration is monotonically increases. var lastUniqueAsyncExpiration = 0; @@ -11363,32 +11790,70 @@ var ReactFiberScheduler = function(config) { // The next fiber with an effect that we're currently committing. var nextEffect = null; - // Keep track of which fibers have captured an error that need to be handled. - // Work is removed from this collection after componentDidCatch is called. - var capturedErrors = null; - // Keep track of which fibers have failed during the current batch of work. - // This is a different set than capturedErrors, because it is not reset until - // the end of the batch. This is needed to propagate errors correctly if a - // subtree fails more than once. - var failedBoundaries = null; - // Error boundaries that captured an error during the current commit. - var commitPhaseBoundaries = null; - var firstUncaughtError = null; - var didFatal = false; - var isCommitting = false; - var isUnmounting = false; + + var isRootReadyForCommit = false; + + var legacyErrorBoundariesThatAlreadyFailed = null; // Used for performance tracking. var interruptedBy = null; + var stashedWorkInProgressProperties = void 0; + var replayUnitOfWork = void 0; + if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) { + stashedWorkInProgressProperties = null; + replayUnitOfWork = function(failedUnitOfWork, isAsync) { + // Retore the original state of the work-in-progress + Object.assign(failedUnitOfWork, stashedWorkInProgressProperties); + switch (failedUnitOfWork.tag) { + case HostRoot: + popHostContainer(failedUnitOfWork); + popTopLevelContextObject(failedUnitOfWork); + break; + case HostComponent: + popHostContext(failedUnitOfWork); + break; + case ClassComponent: + popContextProvider(failedUnitOfWork); + break; + case HostPortal: + popHostContainer(failedUnitOfWork); + break; + case ContextProvider: + popProvider(failedUnitOfWork); + break; + } + // Replay the begin phase. + invokeGuardedCallback$2(null, workLoop, null, isAsync); + if (hasCaughtError()) { + clearCaughtError(); + } else { + // This should be unreachable because the render phase is + // idempotent + } + }; + } + function resetContextStack() { // Reset the stack reset(); // Reset the cursors resetContext(); - resetProviderStack(); resetHostContainer(); + + // TODO: Unify new context implementation with other stacks + resetProviderStack(); + + { + ReactStrictModeWarnings.discardPendingWarnings(); + } + + nextRoot = null; + nextRenderExpirationTime = NoWork; + nextUnitOfWork = null; + + isRootReadyForCommit = false; } function commitAllHostEffects() { @@ -11414,8 +11879,7 @@ var ReactFiberScheduler = function(config) { // 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); + var primaryEffectTag = effectTag & (Placement | Update | Deletion); switch (primaryEffectTag) { case Placement: { commitPlacement(nextEffect); @@ -11445,9 +11909,7 @@ var ReactFiberScheduler = function(config) { break; } case Deletion: { - isUnmounting = true; commitDeletion(nextEffect); - isUnmounting = false; break; } } @@ -11459,7 +11921,11 @@ var ReactFiberScheduler = function(config) { } } - function commitAllLifeCycles() { + function commitAllLifeCycles( + finishedRoot, + currentTime, + committedExpirationTime + ) { { ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings(); @@ -11467,14 +11933,23 @@ var ReactFiberScheduler = function(config) { ReactStrictModeWarnings.flushPendingDeprecationWarnings(); } } - while (nextEffect !== null) { var effectTag = nextEffect.effectTag; if (effectTag & (Update | Callback)) { recordEffect(); var current = nextEffect.alternate; - commitLifeCycles(current, nextEffect); + commitLifeCycles( + finishedRoot, + current, + nextEffect, + currentTime, + committedExpirationTime + ); + } + + if (effectTag & ErrLog) { + commitErrorLogging(nextEffect, onUncaughtError); } if (effectTag & Ref) { @@ -11482,11 +11957,6 @@ var ReactFiberScheduler = function(config) { commitAttachRef(nextEffect); } - if (effectTag & Err) { - recordEffect(); - commitErrorHandling(nextEffect); - } - var next = nextEffect.nextEffect; // Ensure that we clean these up so that we don't accidentally keep them. // I'm not actually sure this matters because we can't reset firstEffect @@ -11499,11 +11969,22 @@ var ReactFiberScheduler = function(config) { } } + function isAlreadyFailedLegacyErrorBoundary(instance) { + return ( + legacyErrorBoundariesThatAlreadyFailed !== null && + legacyErrorBoundariesThatAlreadyFailed.has(instance) + ); + } + + function markLegacyErrorBoundaryAsFailed(instance) { + if (legacyErrorBoundariesThatAlreadyFailed === null) { + legacyErrorBoundariesThatAlreadyFailed = new Set([instance]); + } else { + legacyErrorBoundariesThatAlreadyFailed.add(instance); + } + } + 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(); @@ -11515,7 +11996,15 @@ var ReactFiberScheduler = function(config) { "related to the return field. This error is likely caused by a bug " + "in React. Please file an issue." ); - root.isReadyForCommit = false; + var committedExpirationTime = root.pendingCommitExpirationTime; + invariant( + committedExpirationTime !== NoWork, + "Cannot commit an incomplete root. This error is likely caused by a " + + "bug in React. Please file an issue." + ); + root.pendingCommitExpirationTime = NoWork; + + var currentTime = recalculateCurrentTime(); // Reset this to null before calling lifecycles ReactCurrentOwner.current = null; @@ -11546,12 +12035,12 @@ var ReactFiberScheduler = function(config) { startCommitHostEffectsTimer(); while (nextEffect !== null) { var didError = false; - var _error = void 0; + var error = void 0; { invokeGuardedCallback$2(null, commitAllHostEffects, null); if (hasCaughtError()) { didError = true; - _error = clearCaughtError(); + error = clearCaughtError(); } } if (didError) { @@ -11560,7 +12049,7 @@ var ReactFiberScheduler = function(config) { "Should have next effect. This error is likely caused by a bug " + "in React. Please file an issue." ); - captureError(nextEffect, _error); + onCommitPhaseError(nextEffect, error); // Clean-up if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; @@ -11585,12 +12074,19 @@ var ReactFiberScheduler = function(config) { startCommitLifeCyclesTimer(); while (nextEffect !== null) { var _didError = false; - var _error2 = void 0; + var _error = void 0; { - invokeGuardedCallback$2(null, commitAllLifeCycles, null); + invokeGuardedCallback$2( + null, + commitAllLifeCycles, + null, + root, + currentTime, + committedExpirationTime + ); if (hasCaughtError()) { _didError = true; - _error2 = clearCaughtError(); + _error = clearCaughtError(); } } if (_didError) { @@ -11599,7 +12095,7 @@ var ReactFiberScheduler = function(config) { "Should have next effect. This error is likely caused by a bug " + "in React. Please file an issue." ); - captureError(nextEffect, _error2); + onCommitPhaseError(nextEffect, _error); if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } @@ -11617,26 +12113,12 @@ var ReactFiberScheduler = function(config) { ReactFiberInstrumentation_1.debugTool.onCommitWork(finishedWork); } - // If we caught any errors during this commit, schedule their boundaries - // to update. - if (commitPhaseBoundaries) { - commitPhaseBoundaries.forEach(scheduleErrorRecovery); - commitPhaseBoundaries = null; - } - - if (firstUncaughtError !== null) { - var _error3 = firstUncaughtError; - firstUncaughtError = null; - onUncaughtError(_error3); - } - var remainingTime = root.current.expirationTime; - if (remainingTime === NoWork) { - capturedErrors = null; - failedBoundaries = null; + // If there's no remaining work, we can clear the set of already failed + // error boundaries. + legacyErrorBoundariesThatAlreadyFailed = null; } - return remainingTime; } @@ -11668,6 +12150,9 @@ var ReactFiberScheduler = function(config) { } function completeUnitOfWork(workInProgress) { + // Attempt to complete the current unit of work, then move to the + // next sibling. If there are no more siblings, return to the + // parent fiber. while (true) { // The current, flushed, state of this fiber is the alternate. // Ideally nothing should rely on this, but relying on it here @@ -11677,80 +12162,140 @@ var ReactFiberScheduler = function(config) { { 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) { + if ((workInProgress.effectTag & Incomplete) === NoEffect) { + // This fiber completed. + var next = completeWork( + current, + workInProgress, + nextRenderExpirationTime + ); stopWorkTimer(workInProgress); + resetExpirationTime(workInProgress, nextRenderExpirationTime); + { + ReactDebugCurrentFiber.resetCurrentFiber(); + } + + if (next !== null) { + stopWorkTimer(workInProgress); + if (true && ReactFiberInstrumentation_1.debugTool) { + ReactFiberInstrumentation_1.debugTool.onCompleteWork( + workInProgress + ); + } + // If completing this work spawned new work, do that next. We'll come + // back here again. + return next; + } + + if ( + returnFiber !== null && + // Do not append effects to parents if a sibling failed to complete + (returnFiber.effectTag & Incomplete) === NoEffect + ) { + // Append all the effects of the subtree and this fiber onto the effect + // list of the parent. The completion order of the children affects the + // side-effect order. + if (returnFiber.firstEffect === null) { + returnFiber.firstEffect = workInProgress.firstEffect; + } + if (workInProgress.lastEffect !== null) { + if (returnFiber.lastEffect !== null) { + returnFiber.lastEffect.nextEffect = workInProgress.firstEffect; + } + returnFiber.lastEffect = workInProgress.lastEffect; + } + + // If this fiber had side-effects, we append it AFTER the children's + // side-effects. We can perform certain side-effects earlier if + // needed, by doing multiple passes over the effect list. We don't want + // to schedule our own side-effect on our own list because if end up + // reusing children we'll schedule this effect onto itself since we're + // at the end. + var effectTag = workInProgress.effectTag; + // Skip both NoWork and PerformedWork tags when creating the effect list. + // PerformedWork effect is read by React DevTools but shouldn't be committed. + if (effectTag > PerformedWork) { + if (returnFiber.lastEffect !== null) { + returnFiber.lastEffect.nextEffect = workInProgress; + } else { + returnFiber.firstEffect = workInProgress; + } + returnFiber.lastEffect = workInProgress; + } + } + if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); } - // If completing this work spawned new work, do that next. We'll come - // back here again. - return next; - } - if (returnFiber !== null) { - // Append all the effects of the subtree and this fiber onto the effect - // list of the parent. The completion order of the children affects the - // side-effect order. - if (returnFiber.firstEffect === null) { - returnFiber.firstEffect = workInProgress.firstEffect; + if (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. + isRootReadyForCommit = true; + return null; } - if (workInProgress.lastEffect !== null) { - if (returnFiber.lastEffect !== null) { - returnFiber.lastEffect.nextEffect = workInProgress.firstEffect; - } - returnFiber.lastEffect = workInProgress.lastEffect; - } - - // If this fiber had side-effects, we append it AFTER the children's - // side-effects. We can perform certain side-effects earlier if - // needed, by doing multiple passes over the effect list. We don't want - // to schedule our own side-effect on our own list because if end up - // reusing children we'll schedule this effect onto itself since we're - // at the end. - var effectTag = workInProgress.effectTag; - // Skip both NoWork and PerformedWork tags when creating the effect list. - // PerformedWork effect is read by React DevTools but shouldn't be committed. - if (effectTag > PerformedWork) { - if (returnFiber.lastEffect !== null) { - returnFiber.lastEffect.nextEffect = workInProgress; - } else { - returnFiber.firstEffect = workInProgress; - } - returnFiber.lastEffect = workInProgress; - } - } - - stopWorkTimer(workInProgress); - if (true && ReactFiberInstrumentation_1.debugTool) { - ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); - } - - if (siblingFiber !== null) { - // If there is more work to do in this returnFiber, do that next. - return siblingFiber; - } else if (returnFiber !== null) { - // If there's no more work in this returnFiber. Complete the returnFiber. - workInProgress = returnFiber; - continue; } else { - // We've reached the root. - var root = workInProgress.stateNode; - root.isReadyForCommit = true; - return null; + // This fiber did not complete because something threw. Pop values off + // the stack without entering the complete phase. If this is a boundary, + // capture values if possible. + var _next = unwindWork(workInProgress); + // Because this fiber did not complete, don't reset its expiration time. + if (workInProgress.effectTag & DidCapture) { + // Restarting an error boundary + stopFailedWorkTimer(workInProgress); + } else { + stopWorkTimer(workInProgress); + } + + { + ReactDebugCurrentFiber.resetCurrentFiber(); + } + + 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. + // Since we're restarting, remove anything that is not a host effect + // from the effect tag. + _next.effectTag &= HostEffectMask; + return _next; + } + + if (returnFiber !== null) { + // Mark the parent fiber as incomplete and clear its effect list. + returnFiber.firstEffect = returnFiber.lastEffect = null; + returnFiber.effectTag |= Incomplete; + } + + if (true && ReactFiberInstrumentation_1.debugTool) { + ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); + } + + if (siblingFiber !== null) { + // If there is more work to do in this returnFiber, do that next. + return siblingFiber; + } else if (returnFiber !== null) { + // If there's no more work in this returnFiber. Complete the returnFiber. + workInProgress = returnFiber; + continue; + } else { + return null; + } } } @@ -11773,45 +12318,11 @@ var ReactFiberScheduler = function(config) { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } + if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) { + stashedWorkInProgressProperties = Object.assign({}, workInProgress); + } var next = beginWork(current, workInProgress, nextRenderExpirationTime); - { - ReactDebugCurrentFiber.resetCurrentFiber(); - } - if (true && ReactFiberInstrumentation_1.debugTool) { - ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); - } - if (next === null) { - // If this doesn't spawn new work, complete the current work. - next = completeUnitOfWork(workInProgress); - } - - ReactCurrentOwner.current = null; - - return next; - } - - function performFailedUnitOfWork(workInProgress) { - { - ReactStrictModeWarnings.discardPendingWarnings(); - } - - // The current, flushed, state of this fiber is the alternate. - // Ideally nothing should rely on this, but relying on it here - // means that we don't need an additional field on the work in - // progress. - var current = workInProgress.alternate; - - // See if beginning this work spawns more work. - startWorkTimer(workInProgress); - { - ReactDebugCurrentFiber.setCurrentFiber(workInProgress); - } - var next = beginFailedWork( - current, - workInProgress, - nextRenderExpirationTime - ); { ReactDebugCurrentFiber.resetCurrentFiber(); } @@ -11830,14 +12341,6 @@ var ReactFiberScheduler = function(config) { } function workLoop(isAsync) { - 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(isAsync); - return; - } if (!isAsync) { // Flush all expired work. while (nextUnitOfWork !== null) { @@ -11851,47 +12354,6 @@ var ReactFiberScheduler = function(config) { } } - function slowWorkLoopThatChecksForFailedWork(isAsync) { - if (!isAsync) { - // Flush all expired work. - while (nextUnitOfWork !== null) { - if (hasCapturedError(nextUnitOfWork)) { - // Use a forked version of performUnitOfWork - nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork); - } else { - nextUnitOfWork = performUnitOfWork(nextUnitOfWork); - } - } - } else { - // Flush asynchronous work until the deadline runs out of time. - while (nextUnitOfWork !== null && !shouldYield()) { - if (hasCapturedError(nextUnitOfWork)) { - // Use a forked version of performUnitOfWork - nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork); - } else { - nextUnitOfWork = performUnitOfWork(nextUnitOfWork); - } - } - } - } - - function renderRootCatchBlock(root, failedWork, boundary, isAsync) { - // We're going to restart the error boundary that captured the error. - // Conceptually, we're unwinding the stack. We need to unwind the - // context stack, too. - unwindContexts(failedWork, boundary); - - // Restart the error boundary using a forked version of - // performUnitOfWork that deletes the boundary's children. The entire - // failed subree will be unmounted. During the commit phase, a special - // lifecycle method is called on the error boundary, which triggers - // a re-render. - nextUnitOfWork = performFailedUnitOfWork(boundary); - - // Continue working. - workLoop(isAsync); - } - function renderRoot(root, expirationTime, isAsync) { invariant( !isWorking, @@ -11900,15 +12362,11 @@ var ReactFiberScheduler = function(config) { ); isWorking = true; - // We're about to mutate the work-in-progress tree. If the root was pending - // commit, it no longer is: we'll need to complete it again. - root.isReadyForCommit = false; - // Check if we're starting from a fresh stack, or if we're resuming from // previously yielded work. if ( - root !== nextRoot || expirationTime !== nextRenderExpirationTime || + root !== nextRoot || nextUnitOfWork === null ) { // Reset the stack and start working from the root. @@ -11918,347 +12376,145 @@ var ReactFiberScheduler = function(config) { nextUnitOfWork = createWorkInProgress( nextRoot.current, null, - expirationTime + nextRenderExpirationTime ); + root.pendingCommitExpirationTime = NoWork; } + var didFatal = false; + startWorkLoopTimer(nextUnitOfWork); - var didError = false; - var error = null; - { - invokeGuardedCallback$2(null, workLoop, null, isAsync); - if (hasCaughtError()) { - didError = true; - error = clearCaughtError(); - } - } - - // An error was thrown during the render phase. - while (didError) { - if (didFatal) { - // This was a fatal error. Don't attempt to recover from it. - firstUncaughtError = error; - break; - } - - var failedWork = nextUnitOfWork; - if (failedWork === null) { - // An error was thrown but there's no current unit of work. This can - // happen during the commit phase if there's a bug in the renderer. - didFatal = true; - continue; - } - - // "Capture" the error by finding the nearest boundary. If there is no - // error boundary, we use the root. - var boundary = captureError(failedWork, error); - invariant( - boundary !== null, - "Should have found an error boundary. This error is likely " + - "caused by a bug in React. Please file an issue." - ); - - 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, - isAsync - ); - if (hasCaughtError()) { - didError = true; - error = clearCaughtError(); - continue; + do { + try { + workLoop(isAsync); + } catch (thrownValue) { + if (nextUnitOfWork === null) { + // This is a fatal error. + didFatal = true; + onUncaughtError(thrownValue); + break; } - } - // We're finished working. Exit the error loop. - break; - } - var uncaughtError = firstUncaughtError; + if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) { + var failedUnitOfWork = nextUnitOfWork; + replayUnitOfWork(failedUnitOfWork, isAsync); + } + + var sourceFiber = nextUnitOfWork; + var returnFiber = sourceFiber["return"]; + if (returnFiber === null) { + // This is a fatal error. + didFatal = true; + onUncaughtError(thrownValue); + break; + } + throwException(returnFiber, sourceFiber, thrownValue); + nextUnitOfWork = completeUnitOfWork(sourceFiber); + } + break; + } while (true); // We're done performing work. Time to clean up. stopWorkLoopTimer(interruptedBy); interruptedBy = null; isWorking = false; - didFatal = false; - firstUncaughtError = null; - if (uncaughtError !== null) { - onUncaughtError(uncaughtError); - } - - return root.isReadyForCommit ? root.current.alternate : null; - } - - // Returns the boundary that captured the error, or null if the error is ignored - function captureError(failedWork, error) { - // It is no longer valid because we exited the user code. - ReactCurrentOwner.current = null; - { - ReactDebugCurrentFiber.resetCurrentFiber(); - } - - // Search for the nearest error boundary. - var boundary = null; - - // Passed to logCapturedError() - var errorBoundaryFound = false; - var willRetry = false; - var errorBoundaryName = null; - - // Host containers are a special case. If the failed work itself is a host - // container, then it acts as its own boundary. In all other cases, we - // ignore the work itself and only search through the parents. - if (failedWork.tag === HostRoot) { - boundary = failedWork; - - if (isFailedBoundary(failedWork)) { - // If this root already failed, there must have been an error when - // attempting to unmount it. This is a worst-case scenario and - // should only be possible if there's a bug in the renderer. - didFatal = true; - } - } else { - var node = failedWork["return"]; - while (node !== null && boundary === null) { - if (node.tag === ClassComponent) { - var instance = node.stateNode; - if (typeof instance.componentDidCatch === "function") { - errorBoundaryFound = true; - errorBoundaryName = getComponentName(node); - - // Found an error boundary! - boundary = node; - willRetry = true; - } - } else if (node.tag === HostRoot) { - // Treat the root like a no-op error boundary - boundary = node; - } - - if (isFailedBoundary(node)) { - // This boundary is already in a failed state. - - // If we're currently unmounting, that means this error was - // thrown while unmounting a failed subtree. We should ignore - // the error. - if (isUnmounting) { - return null; - } - - // If we're in the commit phase, we should check to see if - // this boundary already captured an error during this commit. - // This case exists because multiple errors can be thrown during - // a single commit without interruption. - if ( - commitPhaseBoundaries !== null && - (commitPhaseBoundaries.has(node) || - (node.alternate !== null && - commitPhaseBoundaries.has(node.alternate))) - ) { - // If so, we should ignore this error. - return null; - } - - // The error should propagate to the next boundary -— we keep looking. - boundary = null; - willRetry = false; - } - - node = node["return"]; - } - } - - if (boundary !== null) { - // Add to the collection of failed boundaries. This lets us know that - // subsequent errors in this subtree should propagate to the next boundary. - if (failedBoundaries === null) { - failedBoundaries = new Set(); - } - failedBoundaries.add(boundary); - - // This method is unsafe outside of the begin and complete phases. - // We might be in the commit phase when an error is captured. - // The risk is that the return path from this Fiber may not be accurate. - // That risk is acceptable given the benefit of providing users more context. - var _componentStack = getStackAddendumByWorkInProgressFiber(failedWork); - var _componentName = getComponentName(failedWork); - - // Add to the collection of captured errors. This is stored as a global - // map of errors and their component stack location keyed by the boundaries - // that capture them. We mostly use this Map as a Set; it's a Map only to - // avoid adding a field to Fiber to store the error. - if (capturedErrors === null) { - capturedErrors = new Map(); - } - - var capturedError = { - componentName: _componentName, - componentStack: _componentStack, - error: error, - errorBoundary: errorBoundaryFound ? boundary.stateNode : null, - errorBoundaryFound: errorBoundaryFound, - errorBoundaryName: errorBoundaryName, - willRetry: willRetry - }; - - capturedErrors.set(boundary, capturedError); - - try { - logCapturedError(capturedError); - } catch (e) { - // Prevent cycle if logCapturedError() throws. - // A cycle may still occur if logCapturedError renders a component that throws. - var suppressLogging = e && e.suppressReactErrorLogging; - if (!suppressLogging) { - console.error(e); - } - } - - // If we're in the commit phase, defer scheduling an update on the - // boundary until after the commit is complete - if (isCommitting) { - if (commitPhaseBoundaries === null) { - commitPhaseBoundaries = new Set(); - } - commitPhaseBoundaries.add(boundary); + // Yield back to main thread. + if (didFatal) { + // There was a fatal error. + return null; + } else if (nextUnitOfWork === null) { + // We reached the root. + if (isRootReadyForCommit) { + // The root successfully completed. It's ready for commit. + root.pendingCommitExpirationTime = expirationTime; + var finishedWork = root.current.alternate; + return finishedWork; } else { - // Otherwise, schedule an update now. - // TODO: Is this actually necessary during the render phase? Is it - // possible to unwind and continue rendering at the same priority, - // without corrupting internal state? - scheduleErrorRecovery(boundary); - } - return boundary; - } else if (firstUncaughtError === null) { - // If no boundary is found, we'll need to throw the error - firstUncaughtError = error; - } - return null; - } - - function hasCapturedError(fiber) { - // TODO: capturedErrors should store the boundary instance, to avoid needing - // to check the alternate. - return ( - capturedErrors !== null && - (capturedErrors.has(fiber) || - (fiber.alternate !== null && capturedErrors.has(fiber.alternate))) - ); - } - - function isFailedBoundary(fiber) { - // TODO: failedBoundaries should store the boundary instance, to avoid - // needing to check the alternate. - return ( - failedBoundaries !== null && - (failedBoundaries.has(fiber) || - (fiber.alternate !== null && failedBoundaries.has(fiber.alternate))) - ); - } - - function commitErrorHandling(effectfulFiber) { - var capturedError = void 0; - if (capturedErrors !== null) { - capturedError = capturedErrors.get(effectfulFiber); - capturedErrors["delete"](effectfulFiber); - if (capturedError == null) { - if (effectfulFiber.alternate !== null) { - effectfulFiber = effectfulFiber.alternate; - capturedError = capturedErrors.get(effectfulFiber); - capturedErrors["delete"](effectfulFiber); - } - } - } - - 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: + // The root did not complete. invariant( false, - "Invalid type of work. This error is likely caused by a bug in " + - "React. Please file an issue." + "Expired work should have completed. This error is likely caused " + + "by a bug in React. Please file an issue." ); + } + } else { + // There's more work to do, but we ran out of time. Yield back to + // the renderer. + return null; } } - function unwindContexts(from, to) { - var node = from; - while (node !== null) { - switch (node.tag) { + function scheduleCapture(sourceFiber, boundaryFiber, value, expirationTime) { + // TODO: We only support dispatching errors. + var capturedValue = createCapturedValue(value, sourceFiber); + var update = { + expirationTime: expirationTime, + partialState: null, + callback: null, + isReplace: false, + isForced: false, + capturedValue: capturedValue, + next: null + }; + insertUpdateIntoFiber(boundaryFiber, update); + scheduleWork(boundaryFiber, expirationTime); + } + + function dispatch(sourceFiber, value, expirationTime) { + invariant( + !isWorking || isCommitting, + "dispatch: Cannot dispatch during the render phase." + ); + + // TODO: Handle arrays + + var fiber = sourceFiber["return"]; + while (fiber !== null) { + switch (fiber.tag) { case ClassComponent: - popContextProvider(node); - break; - case HostComponent: - popHostContext(node); + var ctor = fiber.type; + var instance = fiber.stateNode; + if ( + typeof ctor.getDerivedStateFromCatch === "function" || + (typeof instance.componentDidCatch === "function" && + !isAlreadyFailedLegacyErrorBoundary(instance)) + ) { + scheduleCapture(sourceFiber, fiber, value, expirationTime); + return; + } break; + // TODO: Handle async boundaries case HostRoot: - popHostContainer(node); - break; - case HostPortal: - popHostContainer(node); - break; - case ContextProvider: - popProvider(node); - break; + scheduleCapture(sourceFiber, fiber, value, expirationTime); + return; } - if (node === to || node.alternate === to) { - stopFailedWorkTimer(node); - break; - } else { - stopWorkTimer(node); - } - node = node["return"]; + fiber = fiber["return"]; + } + + if (sourceFiber.tag === HostRoot) { + // Error was thrown at the root. There is no parent, so the root + // itself should capture it. + scheduleCapture(sourceFiber, sourceFiber, value, expirationTime); } } - function computeAsyncExpiration() { + function onCommitPhaseError(fiber, error) { + return dispatch(fiber, error, Sync); + } + + function computeAsyncExpiration(currentTime) { // 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; + var expirationMs = 5000; + var bucketSizeMs = 250; return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs); } - function computeInteractiveExpiration() { + function computeInteractiveExpiration(currentTime) { // Should complete within ~500ms. 600ms max. - var currentTime = recalculateCurrentTime(); var expirationMs = 500; var bucketSizeMs = 100; return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs); @@ -12266,7 +12522,8 @@ var ReactFiberScheduler = function(config) { // Creates a unique async expiration time. function computeUniqueAsyncExpiration() { - var result = computeAsyncExpiration(); + var currentTime = recalculateCurrentTime(); + var result = computeAsyncExpiration(currentTime); if (result <= lastUniqueAsyncExpiration) { // Since we assume the current time monotonically increases, we only hit // this branch when computeUniqueAsyncExpiration is fired multiple times @@ -12298,10 +12555,12 @@ var ReactFiberScheduler = function(config) { if (fiber.mode & AsyncMode) { if (isBatchingInteractiveUpdates) { // This is an interactive update - expirationTime = computeInteractiveExpiration(); + var currentTime = recalculateCurrentTime(); + expirationTime = computeInteractiveExpiration(currentTime); } else { // This is an async update - expirationTime = computeAsyncExpiration(); + var _currentTime = recalculateCurrentTime(); + expirationTime = computeAsyncExpiration(_currentTime); } } else { // This is a sync update @@ -12326,23 +12585,6 @@ var ReactFiberScheduler = function(config) { 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(); @@ -12374,10 +12616,27 @@ var ReactFiberScheduler = function(config) { if (node["return"] === null) { if (node.tag === HostRoot) { var root = node.stateNode; - - checkRootNeedsClearing(root, fiber, expirationTime); - requestWork(root, expirationTime); - checkRootNeedsClearing(root, fiber, expirationTime); + if ( + !isWorking && + nextRenderExpirationTime !== NoWork && + expirationTime < nextRenderExpirationTime + ) { + // This is an interruption. (Used for performance tracking.) + interruptedBy = fiber; + resetContextStack(); + } + if (nextRoot !== root || !isWorking) { + 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." + ); + } } else { { if (!isErrorRecovery && fiber.tag === ClassComponent) { @@ -12391,27 +12650,23 @@ var ReactFiberScheduler = function(config) { } } - function scheduleErrorRecovery(fiber) { - scheduleWorkImpl(fiber, Sync, true); - } - function recalculateCurrentTime() { // Subtract initial time so it fits inside 32bits - var ms = now() - startTime; - mostRecentCurrentTime = msToExpirationTime(ms); + mostRecentCurrentTimeMs = now() - originalStartTimeMs; + mostRecentCurrentTime = msToExpirationTime(mostRecentCurrentTimeMs); return mostRecentCurrentTime; } function deferredUpdates(fn) { var previousExpirationContext = expirationContext; - expirationContext = computeAsyncExpiration(); + var currentTime = recalculateCurrentTime(); + expirationContext = computeAsyncExpiration(currentTime); try { return fn(); } finally { expirationContext = previousExpirationContext; } } - function syncUpdates(fn, a, b, c, d) { var previousExpirationContext = expirationContext; expirationContext = Sync; @@ -12469,7 +12724,7 @@ var ReactFiberScheduler = function(config) { } // Compute a timeout for the given expiration time. - var currentMs = now() - startTime; + var currentMs = now() - originalStartTimeMs; var expirationMs = expirationTimeToMs(expirationTime); var timeout = expirationMs - currentMs; @@ -12482,16 +12737,35 @@ var ReactFiberScheduler = function(config) { // 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." - ); + addRootToSchedule(root, expirationTime); + + if (isRendering) { + // Prevent reentrancy. Remaining work will be scheduled at the end of + // the currently rendering batch. + return; } + if (isBatchingUpdates) { + // Flush work at the end of the batch. + if (isUnbatchingUpdates) { + // ...unless we're inside unbatchedUpdates, in which case we should + // flush it now. + nextFlushedRoot = root; + nextFlushedExpirationTime = Sync; + performWorkOnRoot(root, Sync, false); + } + return; + } + + // TODO: Get rid of Sync and use current time? + if (expirationTime === Sync) { + performSyncWork(); + } else { + scheduleCallbackWithExpiration(expirationTime); + } + } + + function addRootToSchedule(root, expirationTime) { // Add the root to the schedule. // Check if this root is already part of the schedule. if (root.nextScheduledRoot === null) { @@ -12516,37 +12790,11 @@ var ReactFiberScheduler = function(config) { root.remainingExpirationTime = expirationTime; } } - - if (isRendering) { - // Prevent reentrancy. Remaining work will be scheduled at the end of - // the currently rendering batch. - return; - } - - if (isBatchingUpdates) { - if (isUnbatchingUpdates) { - // Flush work at the end of the batch. - // ...unless we're inside unbatchedUpdates, in which case we should - // flush it now. - nextFlushedRoot = root; - nextFlushedExpirationTime = Sync; - performWorkOnRoot(root, Sync, false); - } - return; - } - - // TODO: Get rid of Sync and use current time? - if (expirationTime === Sync) { - performSyncWork(); - } else { - scheduleCallbackWithExpiration(expirationTime); - } } function findHighestPriorityRoot() { var highestPriorityWork = NoWork; var highestPriorityRoot = null; - if (lastScheduledRoot !== null) { var previousScheduledRoot = lastScheduledRoot; var root = firstScheduledRoot; @@ -12608,7 +12856,8 @@ var ReactFiberScheduler = function(config) { var previousFlushedRoot = nextFlushedRoot; if ( previousFlushedRoot !== null && - previousFlushedRoot === highestPriorityRoot + previousFlushedRoot === highestPriorityRoot && + highestPriorityWork === Sync ) { nestedUpdateCount++; } else { @@ -12683,7 +12932,6 @@ var ReactFiberScheduler = function(config) { // Clean-up. deadline = null; deadlineDidExpire = false; - nestedUpdateCount = 0; finishRendering(); } @@ -12702,6 +12950,8 @@ var ReactFiberScheduler = function(config) { } function finishRendering() { + nestedUpdateCount = 0; + if (completedBatches !== null) { var batches = completedBatches; completedBatches = null; @@ -12719,10 +12969,10 @@ var ReactFiberScheduler = function(config) { } if (hasUnhandledError) { - var _error4 = unhandledError; + var error = unhandledError; unhandledError = null; hasUnhandledError = false; - throw _error4; + throw error; } } @@ -12815,8 +13065,6 @@ var ReactFiberScheduler = function(config) { return true; } - // TODO: Not happy about this hook. Conceptually, renderRoot should return a - // tuple of (isReadyForCommit, didError, error) function onUncaughtError(error) { invariant( nextFlushedRoot !== null, @@ -12933,6 +13181,7 @@ var ReactFiberScheduler = function(config) { } return { + recalculateCurrentTime: recalculateCurrentTime, computeExpirationForFiber: computeExpirationForFiber, scheduleWork: scheduleWork, requestWork: requestWork, @@ -12976,6 +13225,7 @@ var ReactFiberReconciler$1 = function(config) { var _ReactFiberScheduler = ReactFiberScheduler(config), computeUniqueAsyncExpiration = _ReactFiberScheduler.computeUniqueAsyncExpiration, + recalculateCurrentTime = _ReactFiberScheduler.recalculateCurrentTime, computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber, scheduleWork = _ReactFiberScheduler.scheduleWork, requestWork = _ReactFiberScheduler.requestWork, @@ -12989,7 +13239,13 @@ var ReactFiberReconciler$1 = function(config) { interactiveUpdates = _ReactFiberScheduler.interactiveUpdates, flushInteractiveUpdates = _ReactFiberScheduler.flushInteractiveUpdates; - function scheduleRootUpdate(current, element, expirationTime, callback) { + function scheduleRootUpdate( + current, + element, + currentTime, + expirationTime, + callback + ) { { if ( ReactDebugCurrentFiber.phase === "render" && @@ -13024,6 +13280,7 @@ var ReactFiberReconciler$1 = function(config) { callback: callback, isReplace: false, isForced: false, + capturedValue: null, next: null }; insertUpdateIntoFiber(current, update); @@ -13036,6 +13293,7 @@ var ReactFiberReconciler$1 = function(config) { element, container, parentComponent, + currentTime, expirationTime, callback ) { @@ -13061,7 +13319,13 @@ var ReactFiberReconciler$1 = function(config) { container.pendingContext = context; } - return scheduleRootUpdate(current, element, expirationTime, callback); + return scheduleRootUpdate( + current, + element, + currentTime, + expirationTime, + callback + ); } function findHostInstance(fiber) { @@ -13078,17 +13342,34 @@ var ReactFiberReconciler$1 = function(config) { }, updateContainer: function(element, container, parentComponent, callback) { var current = container.current; + var currentTime = recalculateCurrentTime(); var expirationTime = computeExpirationForFiber(current); return updateContainerAtExpirationTime( element, container, parentComponent, + currentTime, + expirationTime, + callback + ); + }, + updateContainerAtExpirationTime: function( + element, + container, + parentComponent, + expirationTime, + callback + ) { + var currentTime = recalculateCurrentTime(); + return updateContainerAtExpirationTime( + element, + container, + parentComponent, + currentTime, expirationTime, callback ); }, - - updateContainerAtExpirationTime: updateContainerAtExpirationTime, flushRoot: flushRoot, @@ -13833,8 +14114,7 @@ var ReactNativeRenderer = { NativeMethodsMixin: NativeMethodsMixin, // Used by react-native-github/Libraries/ components ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin, // requireNativeComponent - ReactGlobalSharedState: ReactGlobalSharedState, // Systrace - ReactNativeComponentTree: ReactNativeComponentTree, // InspectorUtils, ScrollResponder + ReactNativeComponentTree: ReactNativeComponentTree, // ScrollResponder ReactNativePropRegistry: ReactNativePropRegistry, // flattenStyle, Stylesheet TouchHistoryMath: TouchHistoryMath, // PanResponder createReactNativeComponentClass: createReactNativeComponentClass, // RCTText, RCTView, ReactNativeART diff --git a/Libraries/Renderer/ReactNativeRenderer-prod.js b/Libraries/Renderer/ReactNativeRenderer-prod.js index b62e9533249..dd0cd8b9cb5 100644 --- a/Libraries/Renderer/ReactNativeRenderer-prod.js +++ b/Libraries/Renderer/ReactNativeRenderer-prod.js @@ -13,12 +13,12 @@ require("InitializeCore"); var invariant = require("fbjs/lib/invariant"), emptyFunction = require("fbjs/lib/emptyFunction"), - RCTEventEmitter = require("RCTEventEmitter"), UIManager = require("UIManager"), - React = require("react"), + RCTEventEmitter = require("RCTEventEmitter"), TextInputState = require("TextInputState"), deepDiffer = require("deepDiffer"), flattenStyle = require("flattenStyle"), + React = require("react"), emptyObject = require("fbjs/lib/emptyObject"), shallowEqual = require("fbjs/lib/shallowEqual"), ExceptionsManager = require("ExceptionsManager"); @@ -942,6 +942,7 @@ var eventTypes = { nativeEvent, nativeEventTarget ) { + if (null == targetInst) return null; var bubbleDispatchConfig = customBubblingEventTypes[topLevelType], directDispatchConfig = customDirectEventTypes[topLevelType]; invariant( @@ -991,26 +992,45 @@ function getInstanceFromTag(tag) { return "number" === typeof tag ? instanceCache[tag] || null : tag; } var ReactNativeComponentTree = Object.freeze({ - precacheFiberNode: function(hostInst, tag) { - instanceCache[tag] = hostInst; - }, - uncacheFiberNode: uncacheFiberNode, - getClosestInstanceFromNode: getInstanceFromTag, - getInstanceFromNode: getInstanceFromTag, - getNodeFromInstance: function(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; - }, - updateFiberProps: function(tag, props) { - instanceProps[tag] = props; - } - }), - restoreTarget = null, + precacheFiberNode: function(hostInst, tag) { + instanceCache[tag] = hostInst; + }, + uncacheFiberNode: uncacheFiberNode, + getClosestInstanceFromNode: getInstanceFromTag, + getInstanceFromNode: getInstanceFromTag, + getNodeFromInstance: function(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; + }, + updateFiberProps: function(tag, props) { + instanceProps[tag] = props; + } +}); +injection.injectEventPluginOrder([ + "ResponderEventPlugin", + "ReactNativeBridgeEventPlugin" +]); +getFiberCurrentPropsFromNode = + ReactNativeComponentTree.getFiberCurrentPropsFromNode; +getInstanceFromNode = ReactNativeComponentTree.getInstanceFromNode; +getNodeFromInstance = ReactNativeComponentTree.getNodeFromInstance; +ResponderEventPlugin.injection.injectGlobalResponderHandler({ + onChange: function(from, to, blockNativeResponder) { + null !== to + ? UIManager.setJSResponder(to.stateNode._nativeTag, blockNativeResponder) + : UIManager.clearJSResponder(); + } +}); +injection.injectEventPluginsByName({ + ResponderEventPlugin: ResponderEventPlugin, + ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin +}); +var restoreTarget = null, restoreQueue = null; function restoreStateOfTarget(target) { if ((target = getInstanceFromNode(target))) { @@ -1142,25 +1162,6 @@ var ReactNativeEventEmitter = Object.freeze({ } }); RCTEventEmitter.register(ReactNativeEventEmitter); -injection.injectEventPluginOrder([ - "ResponderEventPlugin", - "ReactNativeBridgeEventPlugin" -]); -getFiberCurrentPropsFromNode = - ReactNativeComponentTree.getFiberCurrentPropsFromNode; -getInstanceFromNode = ReactNativeComponentTree.getInstanceFromNode; -getNodeFromInstance = ReactNativeComponentTree.getNodeFromInstance; -ResponderEventPlugin.injection.injectGlobalResponderHandler({ - onChange: function(from, to, blockNativeResponder) { - null !== to - ? UIManager.setJSResponder(to.stateNode._nativeTag, blockNativeResponder) - : UIManager.clearJSResponder(); - } -}); -injection.injectEventPluginsByName({ - ResponderEventPlugin: ResponderEventPlugin, - ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin -}); 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, @@ -1294,12 +1295,6 @@ var TouchHistoryMath = { }, noCentroid: -1 }, - ReactCurrentOwner = - React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner, - ReactGlobalSharedState = Object.freeze({ - ReactCurrentOwner: ReactCurrentOwner, - ReactDebugCurrentFrame: null - }), objects = {}, uniqueID = 1, emptyObject$2 = {}, @@ -1561,6 +1556,8 @@ function mountSafeCallback(context, callback) { } }; } +var ReactCurrentOwner = + React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner; function getComponentName(fiber) { fiber = fiber.type; if ("function" === typeof fiber) return fiber.displayName || fiber.name; @@ -1833,7 +1830,41 @@ function push(cursor, value) { valueStack[index] = cursor.current; cursor.current = value; } +function getStackAddendumByWorkInProgressFiber(workInProgress) { + var info = ""; + do { + a: switch (workInProgress.tag) { + case 0: + case 1: + case 2: + case 5: + var owner = workInProgress._debugOwner, + source = workInProgress._debugSource; + var JSCompiler_inline_result = getComponentName(workInProgress); + var ownerName = null; + owner && (ownerName = getComponentName(owner)); + owner = source; + JSCompiler_inline_result = + "\n in " + + (JSCompiler_inline_result || "Unknown") + + (owner + ? " (at " + + owner.fileName.replace(/^.*[\\\/]/, "") + + ":" + + owner.lineNumber + + ")" + : ownerName ? " (created by " + ownerName + ")" : ""); + break a; + default: + JSCompiler_inline_result = ""; + } + info += JSCompiler_inline_result; + workInProgress = workInProgress["return"]; + } while (workInProgress); + return info; +} var _require = require("ReactFeatureFlags"), + enableGetDerivedStateFromCatch = _require.enableGetDerivedStateFromCatch, debugRenderPhaseSideEffects = _require.debugRenderPhaseSideEffects, debugRenderPhaseSideEffectsForStrictMode = _require.debugRenderPhaseSideEffectsForStrictMode; @@ -2094,7 +2125,8 @@ function createUpdateQueue(baseState) { last: null, callbackList: null, hasForceUpdate: !1, - isInitialized: !1 + isInitialized: !1, + capturedValues: null }; } function insertUpdateIntoQueue(queue, update) { @@ -2107,7 +2139,10 @@ function insertUpdateIntoQueue(queue, update) { ) queue.expirationTime = update.expirationTime; } -function insertUpdateIntoFiber(fiber, update) { +var q1 = void 0, + q2 = void 0; +function ensureUpdateQueues(fiber) { + q1 = q2 = null; var alternateFiber = fiber.alternate, queue1 = fiber.updateQueue; null === queue1 && (queue1 = fiber.updateQueue = createUpdateQueue(null)); @@ -2116,13 +2151,19 @@ function insertUpdateIntoFiber(fiber, update) { null === fiber && (fiber = alternateFiber.updateQueue = createUpdateQueue(null))) : (fiber = null); - fiber = fiber !== queue1 ? fiber : null; - null === fiber - ? insertUpdateIntoQueue(queue1, update) - : null === queue1.last || null === fiber.last - ? (insertUpdateIntoQueue(queue1, update), - insertUpdateIntoQueue(fiber, update)) - : (insertUpdateIntoQueue(queue1, update), (fiber.last = update)); + q1 = queue1; + q2 = fiber !== queue1 ? fiber : null; +} +function insertUpdateIntoFiber(fiber, update) { + ensureUpdateQueues(fiber); + fiber = q1; + var queue2 = q2; + null === queue2 + ? insertUpdateIntoQueue(fiber, update) + : null === fiber.last || null === queue2.last + ? (insertUpdateIntoQueue(fiber, update), + insertUpdateIntoQueue(queue2, update)) + : (insertUpdateIntoQueue(fiber, update), (queue2.last = update)); } function getStateFromUpdate(update, instance, prevState, props) { update = update.partialState; @@ -2146,6 +2187,7 @@ function processUpdateQueue( first: queue.first, last: queue.last, isInitialized: queue.isInitialized, + capturedValues: queue.capturedValues, callbackList: null, hasForceUpdate: !1 }); @@ -2197,6 +2239,11 @@ function processUpdateQueue( null === updateExpirationTime && (updateExpirationTime = queue.callbackList = []), updateExpirationTime.push(update)); + null !== update.capturedValue && + ((updateExpirationTime = queue.capturedValues), + null === updateExpirationTime + ? (queue.capturedValues = [update.capturedValue]) + : updateExpirationTime.push(update.capturedValue)); } update = update.next; } @@ -2204,6 +2251,7 @@ function processUpdateQueue( ? (workInProgress.effectTag |= 32) : null !== queue.first || queue.hasForceUpdate || + null !== queue.capturedValues || (workInProgress.updateQueue = null); didSkip || (queue.baseState = current); return current; @@ -2227,17 +2275,66 @@ function commitCallbacks(queue, context) { _callback.call(context); } } +function callGetDerivedStateFromCatch(ctor, capturedValues) { + for (var resultState = {}, i = 0; i < capturedValues.length; i++) { + var partialState = ctor.getDerivedStateFromCatch.call( + null, + capturedValues[i].value + ); + null !== partialState && + void 0 !== partialState && + Object.assign(resultState, partialState); + } + return resultState; +} function ReactFiberClassComponent( scheduleWork, computeExpirationForFiber, memoizeProps, memoizeState ) { + function checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldState, + newState, + newContext + ) { + if ( + null === oldProps || + (null !== workInProgress.updateQueue && + workInProgress.updateQueue.hasForceUpdate) + ) + return !0; + var instance = workInProgress.stateNode; + workInProgress = workInProgress.type; + return "function" === typeof instance.shouldComponentUpdate + ? instance.shouldComponentUpdate(newProps, newState, newContext) + : workInProgress.prototype && + workInProgress.prototype.isPureReactComponent + ? !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState) + : !0; + } function adoptClassInstance(workInProgress, instance) { instance.updater = updater; workInProgress.stateNode = instance; instance._reactInternalFiber = workInProgress; } + function callComponentWillReceiveProps( + workInProgress, + instance, + newProps, + newContext + ) { + workInProgress = instance.state; + "function" === typeof instance.componentWillReceiveProps && + instance.componentWillReceiveProps(newProps, newContext); + "function" === typeof instance.UNSAFE_componentWillReceiveProps && + instance.UNSAFE_componentWillReceiveProps(newProps, newContext); + instance.state !== workInProgress && + updater.enqueueReplaceState(instance, instance.state, null); + } function callGetDerivedStateFromProps(workInProgress, instance, props) { instance = workInProgress.type; if ("function" === typeof instance.getDerivedStateFromProps) @@ -2269,7 +2366,7 @@ function ReactFiberClassComponent( callback: callback, isReplace: !1, isForced: !1, - nextCallback: null, + capturedValue: null, next: null }); scheduleWork(instance, expirationTime); @@ -2284,7 +2381,7 @@ function ReactFiberClassComponent( callback: callback, isReplace: !0, isForced: !1, - nextCallback: null, + capturedValue: null, next: null }); scheduleWork(instance, expirationTime); @@ -2299,7 +2396,7 @@ function ReactFiberClassComponent( callback: callback, isReplace: !1, isForced: !0, - nextCallback: null, + capturedValue: null, next: null }); scheduleWork(instance, expirationTime); @@ -2340,7 +2437,8 @@ function ReactFiberClassComponent( return ctor; }, mountClassInstance: function(workInProgress, renderExpirationTime) { - var current = workInProgress.alternate, + var ctor = workInProgress.type, + current = workInProgress.alternate, instance = workInProgress.stateNode, props = workInProgress.pendingProps, unmaskedContext = getUnmaskedContext(workInProgress); @@ -2350,20 +2448,20 @@ function ReactFiberClassComponent( instance.context = getMaskedContext(workInProgress, unmaskedContext); ("function" !== typeof instance.UNSAFE_componentWillMount && "function" !== typeof instance.componentWillMount) || - "function" === typeof workInProgress.type.getDerivedStateFromProps || - ((unmaskedContext = instance.state), + "function" === typeof ctor.getDerivedStateFromProps || + ((ctor = instance.state), "function" === typeof instance.componentWillMount && instance.componentWillMount(), "function" === typeof instance.UNSAFE_componentWillMount && instance.UNSAFE_componentWillMount(), - unmaskedContext !== instance.state && + ctor !== instance.state && updater.enqueueReplaceState(instance, instance.state, null), - (unmaskedContext = workInProgress.updateQueue), - null !== unmaskedContext && + (ctor = workInProgress.updateQueue), + null !== ctor && (instance.state = processUpdateQueue( current, workInProgress, - unmaskedContext, + ctor, instance, props, renderExpirationTime @@ -2371,12 +2469,9 @@ function ReactFiberClassComponent( "function" === typeof instance.componentDidMount && (workInProgress.effectTag |= 4); }, - updateClassInstance: function( - current, - workInProgress, - renderExpirationTime - ) { - var instance = workInProgress.stateNode; + resumeMountClassInstance: function(workInProgress, renderExpirationTime) { + var ctor = workInProgress.type, + instance = workInProgress.stateNode; instance.props = workInProgress.memoizedProps; instance.state = workInProgress.memoizedState; var oldProps = workInProgress.memoizedProps, @@ -2386,43 +2481,166 @@ function ReactFiberClassComponent( newUnmaskedContext = getMaskedContext(workInProgress, newUnmaskedContext); ("function" !== typeof instance.UNSAFE_componentWillReceiveProps && "function" !== typeof instance.componentWillReceiveProps) || - "function" === typeof workInProgress.type.getDerivedStateFromProps || - (oldProps === newProps && oldContext === newUnmaskedContext) || - ((oldContext = instance.state), - "function" === typeof instance.componentWillReceiveProps && - instance.componentWillReceiveProps(newProps, newUnmaskedContext), - "function" === typeof instance.UNSAFE_componentWillReceiveProps && - instance.UNSAFE_componentWillReceiveProps( + "function" === typeof ctor.getDerivedStateFromProps || + ((oldProps !== newProps || oldContext !== newUnmaskedContext) && + callComponentWillReceiveProps( + workInProgress, + instance, newProps, newUnmaskedContext - ), - instance.state !== oldContext && - updater.enqueueReplaceState(instance, instance.state, null)); - var partialState = void 0; + )); + oldContext = void 0; oldProps !== newProps && - (partialState = callGetDerivedStateFromProps( + (oldContext = callGetDerivedStateFromProps( + workInProgress, + instance, + newProps + )); + var oldState = workInProgress.memoizedState, + derivedStateFromCatch = void 0; + if (null !== workInProgress.updateQueue) { + renderExpirationTime = processUpdateQueue( + null, + workInProgress, + workInProgress.updateQueue, + instance, + newProps, + renderExpirationTime + ); + var updateQueue = workInProgress.updateQueue; + null !== updateQueue && + null !== updateQueue.capturedValues && + enableGetDerivedStateFromCatch && + "function" === typeof ctor.getDerivedStateFromCatch && + (derivedStateFromCatch = callGetDerivedStateFromCatch( + ctor, + updateQueue.capturedValues + )); + } else renderExpirationTime = oldState; + null !== oldContext && + void 0 !== oldContext && + (renderExpirationTime = + null === renderExpirationTime || void 0 === renderExpirationTime + ? oldContext + : Object.assign({}, renderExpirationTime, oldContext)); + null !== derivedStateFromCatch && + void 0 !== derivedStateFromCatch && + (renderExpirationTime = + null === renderExpirationTime || void 0 === renderExpirationTime + ? derivedStateFromCatch + : Object.assign({}, renderExpirationTime, derivedStateFromCatch)); + if ( + !( + oldProps !== newProps || + oldState !== renderExpirationTime || + didPerformWorkStackCursor.current || + (null !== workInProgress.updateQueue && + workInProgress.updateQueue.hasForceUpdate) + ) + ) + return ( + "function" === typeof instance.componentDidMount && + (workInProgress.effectTag |= 4), + !1 + ); + (oldProps = checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldState, + renderExpirationTime, + newUnmaskedContext + )) + ? (("function" !== typeof instance.UNSAFE_componentWillUpdate && + "function" !== typeof instance.componentWillUpdate) || + "function" === typeof ctor.getDerivedStateFromProps || + ("function" === typeof instance.componentWillUpdate && + instance.componentWillUpdate( + newProps, + renderExpirationTime, + newUnmaskedContext + ), + "function" === typeof instance.UNSAFE_componentWillUpdate && + instance.UNSAFE_componentWillUpdate( + newProps, + renderExpirationTime, + newUnmaskedContext + )), + "function" === typeof instance.componentDidUpdate && + (workInProgress.effectTag |= 4)) + : ("function" === typeof instance.componentDidMount && + (workInProgress.effectTag |= 4), + memoizeProps(workInProgress, newProps), + memoizeState(workInProgress, renderExpirationTime)); + instance.props = newProps; + instance.state = renderExpirationTime; + instance.context = newUnmaskedContext; + return oldProps; + }, + updateClassInstance: function( + current, + workInProgress, + renderExpirationTime + ) { + var ctor = workInProgress.type, + instance = workInProgress.stateNode; + instance.props = workInProgress.memoizedProps; + instance.state = workInProgress.memoizedState; + var oldProps = workInProgress.memoizedProps, + newProps = workInProgress.pendingProps, + oldContext = instance.context, + newUnmaskedContext = getUnmaskedContext(workInProgress); + newUnmaskedContext = getMaskedContext(workInProgress, newUnmaskedContext); + ("function" !== typeof instance.UNSAFE_componentWillReceiveProps && + "function" !== typeof instance.componentWillReceiveProps) || + "function" === typeof ctor.getDerivedStateFromProps || + ((oldProps !== newProps || oldContext !== newUnmaskedContext) && + callComponentWillReceiveProps( + workInProgress, + instance, + newProps, + newUnmaskedContext + )); + var derivedStateFromProps = void 0; + oldProps !== newProps && + (derivedStateFromProps = callGetDerivedStateFromProps( workInProgress, instance, newProps )); oldContext = workInProgress.memoizedState; - renderExpirationTime = - null !== workInProgress.updateQueue - ? processUpdateQueue( - current, - workInProgress, - workInProgress.updateQueue, - instance, - newProps, - renderExpirationTime - ) - : oldContext; - null !== partialState && - void 0 !== partialState && + var derivedStateFromCatch = void 0; + if (null !== workInProgress.updateQueue) { + renderExpirationTime = processUpdateQueue( + current, + workInProgress, + workInProgress.updateQueue, + instance, + newProps, + renderExpirationTime + ); + var updateQueue = workInProgress.updateQueue; + null !== updateQueue && + null !== updateQueue.capturedValues && + enableGetDerivedStateFromCatch && + "function" === typeof ctor.getDerivedStateFromCatch && + (derivedStateFromCatch = callGetDerivedStateFromCatch( + ctor, + updateQueue.capturedValues + )); + } else renderExpirationTime = oldContext; + null !== derivedStateFromProps && + void 0 !== derivedStateFromProps && (renderExpirationTime = null === renderExpirationTime || void 0 === renderExpirationTime - ? partialState - : Object.assign({}, renderExpirationTime, partialState)); + ? derivedStateFromProps + : Object.assign({}, renderExpirationTime, derivedStateFromProps)); + null !== derivedStateFromCatch && + void 0 !== derivedStateFromCatch && + (renderExpirationTime = + null === renderExpirationTime || void 0 === renderExpirationTime + ? derivedStateFromCatch + : Object.assign({}, renderExpirationTime, derivedStateFromCatch)); if ( !( oldProps !== newProps || @@ -2439,33 +2657,17 @@ function ReactFiberClassComponent( (workInProgress.effectTag |= 4), !1 ); - partialState = renderExpirationTime; - if ( - null === oldProps || - (null !== workInProgress.updateQueue && - workInProgress.updateQueue.hasForceUpdate) - ) - partialState = !0; - else { - var instance$jscomp$0 = workInProgress.stateNode, - type = workInProgress.type; - partialState = - "function" === typeof instance$jscomp$0.shouldComponentUpdate - ? instance$jscomp$0.shouldComponentUpdate( - newProps, - partialState, - newUnmaskedContext - ) - : type.prototype && type.prototype.isPureReactComponent - ? !shallowEqual(oldProps, newProps) || - !shallowEqual(oldContext, partialState) - : !0; - } - partialState + (derivedStateFromProps = checkShouldComponentUpdate( + workInProgress, + oldProps, + newProps, + oldContext, + renderExpirationTime, + newUnmaskedContext + )) ? (("function" !== typeof instance.UNSAFE_componentWillUpdate && "function" !== typeof instance.componentWillUpdate) || - "function" === - typeof workInProgress.type.getDerivedStateFromProps || + "function" === typeof ctor.getDerivedStateFromProps || ("function" === typeof instance.componentWillUpdate && instance.componentWillUpdate( newProps, @@ -2489,7 +2691,7 @@ function ReactFiberClassComponent( instance.props = newProps; instance.state = renderExpirationTime; instance.context = newUnmaskedContext; - return partialState; + return derivedStateFromProps; } }; } @@ -3199,7 +3401,19 @@ function ReactFiberBeginWork( computeExpirationForFiber ) { function reconcileChildren(current, workInProgress, nextChildren) { - var renderExpirationTime = workInProgress.expirationTime; + reconcileChildrenAtExpirationTime( + current, + workInProgress, + nextChildren, + workInProgress.expirationTime + ); + } + function reconcileChildrenAtExpirationTime( + current, + workInProgress, + nextChildren, + renderExpirationTime + ) { workInProgress.child = null === current ? mountChildFibers( @@ -3227,22 +3441,43 @@ function ReactFiberBeginWork( current, workInProgress, shouldUpdate, - hasContext + hasContext, + didCaptureError, + renderExpirationTime ) { markRef(current, workInProgress); - if (!shouldUpdate) + if (!shouldUpdate && !didCaptureError) return ( hasContext && invalidateContextProvider(workInProgress, !1), bailoutOnAlreadyFinishedWork(current, workInProgress) ); + var ctor = workInProgress.type; shouldUpdate = workInProgress.stateNode; ReactCurrentOwner.current = workInProgress; - (debugRenderPhaseSideEffects || - (debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & 2)) && - shouldUpdate.render(); - var nextChildren = shouldUpdate.render(); + !didCaptureError || + (enableGetDerivedStateFromCatch && + "function" === typeof ctor.getDerivedStateFromCatch) + ? ((debugRenderPhaseSideEffects || + (debugRenderPhaseSideEffectsForStrictMode && + workInProgress.mode & 2)) && + shouldUpdate.render(), + (ctor = shouldUpdate.render())) + : (ctor = null); workInProgress.effectTag |= 1; - reconcileChildren(current, workInProgress, nextChildren); + didCaptureError && + (reconcileChildrenAtExpirationTime( + current, + workInProgress, + null, + renderExpirationTime + ), + (workInProgress.child = null)); + reconcileChildrenAtExpirationTime( + current, + workInProgress, + ctor, + renderExpirationTime + ); workInProgress.memoizedState = shouldUpdate.state; workInProgress.memoizedProps = shouldUpdate.props; hasContext && invalidateContextProvider(workInProgress, !0); @@ -3342,25 +3577,6 @@ function ReactFiberBeginWork( } return workInProgress.child; } - function bailoutOnLowPriority(current, workInProgress) { - switch (workInProgress.tag) { - case 3: - pushHostRootContext(workInProgress); - break; - case 2: - pushContextProvider(workInProgress); - break; - case 4: - pushHostContainer( - workInProgress, - workInProgress.stateNode.containerInfo - ); - break; - case 13: - pushProvider(workInProgress); - } - return null; - } var shouldSetTextContent = config.shouldSetTextContent, shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree, pushHostContext = hostContext.pushHostContext, @@ -3383,14 +3599,32 @@ function ReactFiberBeginWork( callGetDerivedStateFromProps = config.callGetDerivedStateFromProps, constructClassInstance = config.constructClassInstance, mountClassInstance = config.mountClassInstance, + resumeMountClassInstance = config.resumeMountClassInstance, updateClassInstance = config.updateClassInstance; return { beginWork: function(current, workInProgress, renderExpirationTime) { if ( 0 === workInProgress.expirationTime || workInProgress.expirationTime > renderExpirationTime - ) - return bailoutOnLowPriority(current, workInProgress); + ) { + switch (workInProgress.tag) { + case 3: + pushHostRootContext(workInProgress); + break; + case 2: + pushContextProvider(workInProgress); + break; + case 4: + pushHostContainer( + workInProgress, + workInProgress.stateNode.containerInfo + ); + break; + case 13: + pushProvider(workInProgress); + } + return null; + } switch (workInProgress.tag) { case 0: invariant( @@ -3431,7 +3665,9 @@ function ReactFiberBeginWork( current, workInProgress, !0, - props + props, + !1, + renderExpirationTime ))) : ((workInProgress.tag = 1), reconcileChildren(current, workInProgress, fn), @@ -3458,92 +3694,117 @@ function ReactFiberBeginWork( current ); case 2: - return ( - (props = pushContextProvider(workInProgress)), - (fn = void 0), - null === current - ? workInProgress.stateNode - ? invariant(!1, "Resuming work not yet implemented.") - : (constructClassInstance( - workInProgress, - workInProgress.pendingProps - ), - mountClassInstance(workInProgress, renderExpirationTime), - (fn = !0)) - : (fn = updateClassInstance( - current, + props = pushContextProvider(workInProgress); + null === current + ? null === workInProgress.stateNode + ? (constructClassInstance( + workInProgress, + workInProgress.pendingProps + ), + mountClassInstance(workInProgress, renderExpirationTime), + (fn = !0)) + : (fn = resumeMountClassInstance( workInProgress, renderExpirationTime - )), - finishClassComponent(current, workInProgress, fn, props) + )) + : (fn = updateClassInstance( + current, + workInProgress, + renderExpirationTime + )); + unmaskedContext = !1; + var updateQueue = workInProgress.updateQueue; + null !== updateQueue && + null !== updateQueue.capturedValues && + (unmaskedContext = fn = !0); + return finishClassComponent( + current, + workInProgress, + fn, + props, + unmaskedContext, + renderExpirationTime ); case 3: - return ( - pushHostRootContext(workInProgress), - (props = workInProgress.updateQueue), - null !== props - ? ((fn = workInProgress.memoizedState), - (props = processUpdateQueue( - current, + a: if ( + (pushHostRootContext(workInProgress), + (fn = workInProgress.updateQueue), + null !== fn) + ) { + unmaskedContext = workInProgress.memoizedState; + props = processUpdateQueue( + current, + workInProgress, + fn, + null, + null, + renderExpirationTime + ); + workInProgress.memoizedState = props; + fn = workInProgress.updateQueue; + if (null !== fn && null !== fn.capturedValues) fn = null; + else if (unmaskedContext === props) { + resetHydrationState(); + current = bailoutOnAlreadyFinishedWork(current, workInProgress); + break a; + } else fn = props.element; + unmaskedContext = workInProgress.stateNode; + (null === current || null === current.child) && + unmaskedContext.hydrate && + enterHydrationState(workInProgress) + ? ((workInProgress.effectTag |= 2), + (workInProgress.child = mountChildFibers( workInProgress, - props, - null, null, + fn, renderExpirationTime - )), - fn === props - ? (resetHydrationState(), - (current = bailoutOnAlreadyFinishedWork( - current, - workInProgress - ))) - : ((fn = props.element), - (unmaskedContext = workInProgress.stateNode), - (null === current || null === current.child) && - unmaskedContext.hydrate && - enterHydrationState(workInProgress) - ? ((workInProgress.effectTag |= 2), - (workInProgress.child = mountChildFibers( - workInProgress, - null, - fn, - renderExpirationTime - ))) - : (resetHydrationState(), - reconcileChildren(current, workInProgress, fn)), - (workInProgress.memoizedState = props), - (current = workInProgress.child))) + ))) : (resetHydrationState(), - (current = bailoutOnAlreadyFinishedWork( - current, - workInProgress - ))), - current - ); + reconcileChildren(current, workInProgress, fn)); + workInProgress.memoizedState = props; + current = workInProgress.child; + } else + resetHydrationState(), + (current = bailoutOnAlreadyFinishedWork(current, workInProgress)); + return current; case 5: - pushHostContext(workInProgress); - null === current && tryToClaimNextHydratableInstance(workInProgress); - props = workInProgress.type; - var memoizedProps = workInProgress.memoizedProps; - fn = workInProgress.pendingProps; - unmaskedContext = null !== current ? current.memoizedProps : null; - didPerformWorkStackCursor.current || memoizedProps !== fn - ? ((memoizedProps = fn.children), - shouldSetTextContent(props, fn) - ? (memoizedProps = null) - : unmaskedContext && - shouldSetTextContent(props, unmaskedContext) && - (workInProgress.effectTag |= 16), - markRef(current, workInProgress), - 1073741823 !== renderExpirationTime && - workInProgress.mode & 1 && - shouldDeprioritizeSubtree(props, fn) - ? ((workInProgress.expirationTime = 1073741823), - (current = null)) - : (reconcileChildren(current, workInProgress, memoizedProps), - (workInProgress.memoizedProps = fn), - (current = workInProgress.child))) - : (current = bailoutOnAlreadyFinishedWork(current, workInProgress)); + a: { + pushHostContext(workInProgress); + null === current && + tryToClaimNextHydratableInstance(workInProgress); + props = workInProgress.type; + updateQueue = workInProgress.memoizedProps; + fn = workInProgress.pendingProps; + unmaskedContext = null !== current ? current.memoizedProps : null; + if (!didPerformWorkStackCursor.current && updateQueue === fn) { + if ( + (updateQueue = + workInProgress.mode & 1 && + shouldDeprioritizeSubtree(props, fn)) + ) + workInProgress.expirationTime = 1073741823; + if (!updateQueue || 1073741823 !== renderExpirationTime) { + current = bailoutOnAlreadyFinishedWork(current, workInProgress); + break a; + } + } + updateQueue = fn.children; + shouldSetTextContent(props, fn) + ? (updateQueue = null) + : unmaskedContext && + shouldSetTextContent(props, unmaskedContext) && + (workInProgress.effectTag |= 16); + markRef(current, workInProgress); + 1073741823 !== renderExpirationTime && + workInProgress.mode & 1 && + shouldDeprioritizeSubtree(props, fn) + ? ((workInProgress.expirationTime = 1073741823), + (workInProgress.memoizedProps = fn), + (current = null)) + : (reconcileChildren(current, workInProgress, updateQueue), + (workInProgress.memoizedProps = fn), + (current = workInProgress.child)); + } return current; case 6: return ( @@ -3648,28 +3909,28 @@ function ReactFiberBeginWork( unmaskedContext = workInProgress.memoizedProps; if (didPerformWorkStackCursor.current || unmaskedContext !== fn) { workInProgress.memoizedProps = fn; - memoizedProps = fn.value; - if (null === unmaskedContext) memoizedProps = 1073741823; + updateQueue = fn.value; + if (null === unmaskedContext) updateQueue = 1073741823; else { var oldValue = unmaskedContext.value; - (oldValue === memoizedProps && - (0 !== oldValue || 1 / oldValue === 1 / memoizedProps)) || - (oldValue !== oldValue && memoizedProps !== memoizedProps) - ? (memoizedProps = 0) - : ((memoizedProps = + (oldValue === updateQueue && + (0 !== oldValue || 1 / oldValue === 1 / updateQueue)) || + (oldValue !== oldValue && updateQueue !== updateQueue) + ? (updateQueue = 0) + : ((updateQueue = "function" === typeof props.calculateChangedBits - ? props.calculateChangedBits(oldValue, memoizedProps) + ? props.calculateChangedBits(oldValue, updateQueue) : 1073741823), - (memoizedProps |= 0), - 0 !== memoizedProps && + (updateQueue |= 0), + 0 !== updateQueue && propagateContextChange( workInProgress, props, - memoizedProps, + updateQueue, renderExpirationTime )); } - workInProgress.stateNode = memoizedProps; + workInProgress.stateNode = updateQueue; pushProvider(workInProgress); null !== unmaskedContext && unmaskedContext.children === fn.children ? (current = bailoutOnAlreadyFinishedWork( @@ -3687,12 +3948,12 @@ function ReactFiberBeginWork( unmaskedContext = workInProgress.type; fn = workInProgress.pendingProps; props = unmaskedContext.currentValue; - memoizedProps = unmaskedContext.changedBits; - 0 !== memoizedProps && + updateQueue = unmaskedContext.changedBits; + 0 !== updateQueue && propagateContextChange( workInProgress, unmaskedContext, - memoizedProps, + updateQueue, renderExpirationTime ); renderExpirationTime = fn.observedBits; @@ -3700,6 +3961,11 @@ function ReactFiberBeginWork( renderExpirationTime = 1073741823; workInProgress.stateNode = renderExpirationTime; renderExpirationTime = fn.children; + "function" !== typeof renderExpirationTime && + invariant( + !1, + "A context consumer was rendered with multiple children, or a child that isn't a function. A context consumer expects a single child that is a function. If you did pass a function, make sure there is no trailing or leading whitespace around it." + ); renderExpirationTime = renderExpirationTime(props); reconcileChildren(current, workInProgress, renderExpirationTime); return workInProgress.child; @@ -3709,47 +3975,6 @@ function ReactFiberBeginWork( "Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue." ); } - }, - beginFailedWork: function(current, workInProgress, renderExpirationTime) { - switch (workInProgress.tag) { - case 2: - pushContextProvider(workInProgress); - break; - case 3: - pushHostRootContext(workInProgress); - break; - default: - invariant( - !1, - "Invalid type of work. This error is likely caused by a bug in React. Please file an issue." - ); - } - workInProgress.effectTag |= 64; - null === current - ? (workInProgress.child = null) - : workInProgress.child !== current.child && - (workInProgress.child = current.child); - if ( - 0 === workInProgress.expirationTime || - workInProgress.expirationTime > renderExpirationTime - ) - return bailoutOnLowPriority(current, workInProgress); - workInProgress.firstEffect = null; - workInProgress.lastEffect = null; - workInProgress.child = - null === current - ? mountChildFibers(workInProgress, null, null, renderExpirationTime) - : reconcileChildFibers( - workInProgress, - current.child, - null, - renderExpirationTime - ); - 2 === workInProgress.tag && - ((current = workInProgress.stateNode), - (workInProgress.memoizedProps = current.props), - (workInProgress.memoizedState = current.state)); - return workInProgress.child; } }; } @@ -3794,7 +4019,18 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { case 1: return null; case 2: - return popContextProvider(workInProgress), null; + return ( + popContextProvider(workInProgress), + (current = workInProgress.stateNode), + (newProps = workInProgress.updateQueue), + null !== newProps && + null !== newProps.capturedValues && + ((workInProgress.effectTag &= -65), + "function" === typeof current.componentDidCatch + ? (workInProgress.effectTag |= 256) + : (newProps.capturedValues = null)), + null + ); case 3: popHostContainer(workInProgress); pop(didPerformWorkStackCursor, workInProgress); @@ -3806,6 +4042,10 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { if (null === current || null === current.child) popHydrationState(workInProgress), (workInProgress.effectTag &= -3); updateHostContainer(workInProgress); + current = workInProgress.updateQueue; + null !== current && + null !== current.capturedValues && + (workInProgress.effectTag |= 256); return null; case 5: popHostContext(workInProgress); @@ -3813,10 +4053,10 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { var type = workInProgress.type; if (null !== current && null != workInProgress.stateNode) { var oldProps = current.memoizedProps, - instance = workInProgress.stateNode, + _instance = workInProgress.stateNode, currentHostContext = getHostContext(); - instance = prepareUpdate( - instance, + _instance = prepareUpdate( + _instance, type, oldProps, newProps, @@ -3826,7 +4066,7 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { updateHostComponent( current, workInProgress, - instance, + _instance, type, oldProps, newProps, @@ -4006,7 +4246,121 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) { } }; } -function ReactFiberCommitWork(config, captureError) { +function ReactFiberUnwindWork( + hostContext, + scheduleWork, + isAlreadyFailedLegacyErrorBoundary +) { + var popHostContainer = hostContext.popHostContainer, + popHostContext = hostContext.popHostContext; + return { + throwException: function(returnFiber, sourceFiber, rawValue) { + sourceFiber.effectTag |= 512; + sourceFiber.firstEffect = sourceFiber.lastEffect = null; + sourceFiber = { + value: rawValue, + source: sourceFiber, + stack: getStackAddendumByWorkInProgressFiber(sourceFiber) + }; + do { + switch (returnFiber.tag) { + case 3: + ensureUpdateQueues(returnFiber); + returnFiber.updateQueue.capturedValues = [sourceFiber]; + returnFiber.effectTag |= 1024; + return; + case 2: + rawValue = returnFiber.type; + var _instance = returnFiber.stateNode; + if ( + 0 === (returnFiber.effectTag & 64) && + (("function" === typeof rawValue.getDerivedStateFromCatch && + enableGetDerivedStateFromCatch) || + (null !== _instance && + "function" === typeof _instance.componentDidCatch && + !isAlreadyFailedLegacyErrorBoundary(_instance))) + ) { + ensureUpdateQueues(returnFiber); + rawValue = returnFiber.updateQueue; + _instance = rawValue.capturedValues; + null === _instance + ? (rawValue.capturedValues = [sourceFiber]) + : _instance.push(sourceFiber); + returnFiber.effectTag |= 1024; + return; + } + } + returnFiber = returnFiber["return"]; + } while (null !== returnFiber); + }, + unwindWork: function(workInProgress) { + switch (workInProgress.tag) { + case 2: + popContextProvider(workInProgress); + var effectTag = workInProgress.effectTag; + return effectTag & 1024 + ? ((workInProgress.effectTag = (effectTag & -1025) | 64), + workInProgress) + : null; + case 3: + return ( + popHostContainer(workInProgress), + pop(didPerformWorkStackCursor, workInProgress), + pop(contextStackCursor, workInProgress), + (effectTag = workInProgress.effectTag), + effectTag & 1024 + ? ((workInProgress.effectTag = (effectTag & -1025) | 64), + workInProgress) + : null + ); + case 5: + return popHostContext(workInProgress), null; + case 4: + return popHostContainer(workInProgress), null; + case 13: + return popProvider(workInProgress), null; + default: + return null; + } + } + }; +} +function logError(boundary, errorInfo) { + var source = errorInfo.source, + stack = errorInfo.stack; + null === stack && (stack = getStackAddendumByWorkInProgressFiber(source)); + null !== source && getComponentName(source); + errorInfo = errorInfo.value; + stack = null !== stack ? stack : ""; + null !== boundary && getComponentName(boundary); + try { + if (errorInfo instanceof Error) { + var message = errorInfo.message, + name = errorInfo.name; + var errorToHandle = errorInfo; + try { + errorToHandle.message = + (message ? name + ": " + message : name) + + "\n\nThis error is located at:" + + stack; + } catch (e) {} + } else + errorToHandle = + "string" === typeof errorInfo + ? Error(errorInfo + "\n\nThis error is located at:" + stack) + : Error("Unspecified error at:" + stack); + ExceptionsManager.handleException(errorToHandle, !1); + } catch (e) { + (e && e.suppressReactErrorLogging) || console.error(e); + } +} +function ReactFiberCommitWork( + config, + captureError, + scheduleWork, + computeExpirationForFiber, + markLegacyErrorBoundaryAsFailed +) { function safelyDetachRef(current) { var ref = current.ref; if (null !== ref) @@ -4023,12 +4377,12 @@ function ReactFiberCommitWork(config, captureError) { switch (current.tag) { case 2: safelyDetachRef(current); - var instance = current.stateNode; - if ("function" === typeof instance.componentWillUnmount) + var _instance6 = current.stateNode; + if ("function" === typeof _instance6.componentWillUnmount) try { - (instance.props = current.memoizedProps), - (instance.state = current.memoizedState), - instance.componentWillUnmount(); + (_instance6.props = current.memoizedProps), + (_instance6.state = current.memoizedState), + _instance6.componentWillUnmount(); } catch (unmountError) { captureError(current, unmountError); } @@ -4243,8 +4597,8 @@ function ReactFiberCommitWork(config, captureError) { case 2: break; case 5: - var instance = finishedWork.stateNode; - if (null != instance) { + var _instance7 = finishedWork.stateNode; + if (null != _instance7) { var newProps = finishedWork.memoizedProps; current = null !== current ? current.memoizedProps : newProps; var type = finishedWork.type, @@ -4252,7 +4606,7 @@ function ReactFiberCommitWork(config, captureError) { finishedWork.updateQueue = null; null !== updatePayload && commitUpdate( - instance, + _instance7, updatePayload, type, current, @@ -4266,11 +4620,11 @@ function ReactFiberCommitWork(config, captureError) { null !== finishedWork.stateNode, "This should have a text node initialized. This error is likely caused by a bug in React. Please file an issue." ); - instance = finishedWork.memoizedProps; + _instance7 = finishedWork.memoizedProps; commitTextUpdate( finishedWork.stateNode, - null !== current ? current.memoizedProps : instance, - instance + null !== current ? current.memoizedProps : _instance7, + _instance7 ); break; case 3: @@ -4282,46 +4636,48 @@ function ReactFiberCommitWork(config, captureError) { ); } }, - commitLifeCycles: function(current, finishedWork) { + commitLifeCycles: function(finishedRoot, current, finishedWork) { switch (finishedWork.tag) { case 2: - var instance = finishedWork.stateNode; + finishedRoot = finishedWork.stateNode; if (finishedWork.effectTag & 4) if (null === current) - (instance.props = finishedWork.memoizedProps), - (instance.state = finishedWork.memoizedState), - instance.componentDidMount(); + (finishedRoot.props = finishedWork.memoizedProps), + (finishedRoot.state = finishedWork.memoizedState), + finishedRoot.componentDidMount(); else { var prevProps = current.memoizedProps; current = current.memoizedState; - instance.props = finishedWork.memoizedProps; - instance.state = finishedWork.memoizedState; - instance.componentDidUpdate(prevProps, current); + finishedRoot.props = finishedWork.memoizedProps; + finishedRoot.state = finishedWork.memoizedState; + finishedRoot.componentDidUpdate(prevProps, current); } finishedWork = finishedWork.updateQueue; - null !== finishedWork && commitCallbacks(finishedWork, instance); + null !== finishedWork && commitCallbacks(finishedWork, finishedRoot); break; case 3: - instance = finishedWork.updateQueue; - if (null !== instance) { - current = null; + current = finishedWork.updateQueue; + if (null !== current) { + finishedRoot = null; if (null !== finishedWork.child) switch (finishedWork.child.tag) { case 5: - current = getPublicInstance(finishedWork.child.stateNode); + finishedRoot = getPublicInstance( + finishedWork.child.stateNode + ); break; case 2: - current = finishedWork.child.stateNode; + finishedRoot = finishedWork.child.stateNode; } - commitCallbacks(instance, current); + commitCallbacks(current, finishedRoot); } break; case 5: - instance = finishedWork.stateNode; + finishedRoot = finishedWork.stateNode; null === current && finishedWork.effectTag & 4 && commitMount( - instance, + finishedRoot, finishedWork.type, finishedWork.memoizedProps, finishedWork @@ -4338,16 +4694,59 @@ function ReactFiberCommitWork(config, captureError) { ); } }, + commitErrorLogging: function(finishedWork, onUncaughtError) { + switch (finishedWork.tag) { + case 2: + var ctor = finishedWork.type; + onUncaughtError = finishedWork.stateNode; + var updateQueue = finishedWork.updateQueue; + invariant( + null !== updateQueue && null !== updateQueue.capturedValues, + "An error logging effect should not have been scheduled if no errors were captured. This error is likely caused by a bug in React. Please file an issue." + ); + var capturedErrors = updateQueue.capturedValues; + updateQueue.capturedValues = null; + "function" !== typeof ctor.getDerivedStateFromCatch && + markLegacyErrorBoundaryAsFailed(onUncaughtError); + onUncaughtError.props = finishedWork.memoizedProps; + onUncaughtError.state = finishedWork.memoizedState; + for (ctor = 0; ctor < capturedErrors.length; ctor++) { + updateQueue = capturedErrors[ctor]; + var _error = updateQueue.value; + logError(finishedWork, updateQueue); + onUncaughtError.componentDidCatch(_error); + } + break; + case 3: + ctor = finishedWork.updateQueue; + invariant( + null !== ctor && null !== ctor.capturedValues, + "An error logging effect should not have been scheduled if no errors were captured. This error is likely caused by a bug in React. Please file an issue." + ); + capturedErrors = ctor.capturedValues; + ctor.capturedValues = null; + for (ctor = 0; ctor < capturedErrors.length; ctor++) + (updateQueue = capturedErrors[ctor]), + logError(finishedWork, updateQueue), + onUncaughtError(updateQueue.value); + break; + default: + invariant( + !1, + "This unit of work tag cannot capture errors. This error is likely caused by a bug in React. Please file an issue." + ); + } + }, commitAttachRef: function(finishedWork) { var ref = finishedWork.ref; if (null !== ref) { - var instance = finishedWork.stateNode; + var _instance5 = finishedWork.stateNode; switch (finishedWork.tag) { case 5: - finishedWork = getPublicInstance(instance); + finishedWork = getPublicInstance(_instance5); break; default: - finishedWork = instance; + finishedWork = _instance5; } "function" === typeof ref ? ref(finishedWork) @@ -4570,55 +4969,101 @@ function ReactFiberHydrationContext(config) { }; } function ReactFiberScheduler(config) { + function resetContextStack() { + for (; -1 < index; ) (valueStack[index] = null), index--; + previousContext = emptyObject; + contextStackCursor.current = emptyObject; + didPerformWorkStackCursor.current = !1; + resetHostContainer(); + for (var i = index$1; -1 < i; i--) { + var context = stack[i].type.context; + context.currentValue = context.defaultValue; + context.changedBits = 0; + changedBitsStack[i] = null; + currentValueStack[i] = null; + stack[i] = null; + } + index$1 = -1; + nextRoot = null; + nextRenderExpirationTime = 0; + nextUnitOfWork = null; + isRootReadyForCommit = !1; + } + function isAlreadyFailedLegacyErrorBoundary(instance) { + return ( + null !== legacyErrorBoundariesThatAlreadyFailed && + legacyErrorBoundariesThatAlreadyFailed.has(instance) + ); + } function completeUnitOfWork(workInProgress$jscomp$0) { for (;;) { - var next = completeWork( - workInProgress$jscomp$0.alternate, - workInProgress$jscomp$0, - nextRenderExpirationTime - ), + var current = workInProgress$jscomp$0.alternate, returnFiber = workInProgress$jscomp$0["return"], siblingFiber = workInProgress$jscomp$0.sibling; - var workInProgress = workInProgress$jscomp$0; - if ( - 1073741823 === nextRenderExpirationTime || - 1073741823 !== workInProgress.expirationTime - ) { - if (2 !== workInProgress.tag && 3 !== workInProgress.tag) - var newExpirationTime = 0; - else - (newExpirationTime = workInProgress.updateQueue), - (newExpirationTime = - null === newExpirationTime - ? 0 - : newExpirationTime.expirationTime); - for (var child = workInProgress.child; null !== child; ) - 0 !== child.expirationTime && - (0 === newExpirationTime || - newExpirationTime > child.expirationTime) && - (newExpirationTime = child.expirationTime), - (child = child.sibling); - workInProgress.expirationTime = newExpirationTime; - } - if (null !== next) return next; - null !== returnFiber && - (null === returnFiber.firstEffect && - (returnFiber.firstEffect = workInProgress$jscomp$0.firstEffect), - null !== workInProgress$jscomp$0.lastEffect && - (null !== returnFiber.lastEffect && - (returnFiber.lastEffect.nextEffect = - workInProgress$jscomp$0.firstEffect), - (returnFiber.lastEffect = workInProgress$jscomp$0.lastEffect)), - 1 < workInProgress$jscomp$0.effectTag && - (null !== returnFiber.lastEffect - ? (returnFiber.lastEffect.nextEffect = workInProgress$jscomp$0) - : (returnFiber.firstEffect = workInProgress$jscomp$0), - (returnFiber.lastEffect = workInProgress$jscomp$0))); - if (null !== siblingFiber) return siblingFiber; - if (null !== returnFiber) workInProgress$jscomp$0 = returnFiber; - else { - workInProgress$jscomp$0.stateNode.isReadyForCommit = !0; - break; + if (0 === (workInProgress$jscomp$0.effectTag & 512)) { + current = completeWork( + current, + workInProgress$jscomp$0, + nextRenderExpirationTime + ); + var workInProgress = workInProgress$jscomp$0; + if ( + 1073741823 === nextRenderExpirationTime || + 1073741823 !== workInProgress.expirationTime + ) { + b: switch (workInProgress.tag) { + case 3: + case 2: + var newExpirationTime = workInProgress.updateQueue; + newExpirationTime = + null === newExpirationTime + ? 0 + : newExpirationTime.expirationTime; + break b; + default: + newExpirationTime = 0; + } + for (var child = workInProgress.child; null !== child; ) + 0 !== child.expirationTime && + (0 === newExpirationTime || + newExpirationTime > child.expirationTime) && + (newExpirationTime = child.expirationTime), + (child = child.sibling); + workInProgress.expirationTime = newExpirationTime; + } + if (null !== current) return current; + null !== returnFiber && + 0 === (returnFiber.effectTag & 512) && + (null === returnFiber.firstEffect && + (returnFiber.firstEffect = workInProgress$jscomp$0.firstEffect), + null !== workInProgress$jscomp$0.lastEffect && + (null !== returnFiber.lastEffect && + (returnFiber.lastEffect.nextEffect = + workInProgress$jscomp$0.firstEffect), + (returnFiber.lastEffect = workInProgress$jscomp$0.lastEffect)), + 1 < workInProgress$jscomp$0.effectTag && + (null !== returnFiber.lastEffect + ? (returnFiber.lastEffect.nextEffect = workInProgress$jscomp$0) + : (returnFiber.firstEffect = workInProgress$jscomp$0), + (returnFiber.lastEffect = workInProgress$jscomp$0))); + if (null !== siblingFiber) return siblingFiber; + if (null !== returnFiber) workInProgress$jscomp$0 = returnFiber; + else { + isRootReadyForCommit = !0; + break; + } + } else { + workInProgress$jscomp$0 = unwindWork(workInProgress$jscomp$0); + if (null !== workInProgress$jscomp$0) + return ( + (workInProgress$jscomp$0.effectTag &= 511), workInProgress$jscomp$0 + ); + null !== returnFiber && + ((returnFiber.firstEffect = returnFiber.lastEffect = null), + (returnFiber.effectTag |= 512)); + if (null !== siblingFiber) return siblingFiber; + if (null !== returnFiber) workInProgress$jscomp$0 = returnFiber; + else break; } } return null; @@ -4633,263 +5078,114 @@ function ReactFiberScheduler(config) { ReactCurrentOwner.current = null; return next; } - function performFailedUnitOfWork(workInProgress) { - var next = beginFailedWork( - workInProgress.alternate, - workInProgress, - nextRenderExpirationTime - ); - null === next && (next = completeUnitOfWork(workInProgress)); - ReactCurrentOwner.current = null; - return next; - } - function workLoop(isAsync) { - if (null !== capturedErrors) - if (isAsync) - for (; null !== nextUnitOfWork && !shouldYield(); ) - nextUnitOfWork = hasCapturedError(nextUnitOfWork) - ? performFailedUnitOfWork(nextUnitOfWork) - : performUnitOfWork(nextUnitOfWork); - else - for (; null !== nextUnitOfWork; ) - nextUnitOfWork = hasCapturedError(nextUnitOfWork) - ? performFailedUnitOfWork(nextUnitOfWork) - : performUnitOfWork(nextUnitOfWork); - else if (isAsync) - for (; null !== nextUnitOfWork && !shouldYield(); ) - nextUnitOfWork = performUnitOfWork(nextUnitOfWork); - else - for (; null !== nextUnitOfWork; ) - nextUnitOfWork = performUnitOfWork(nextUnitOfWork); - } function renderRoot(root, expirationTime, isAsync) { invariant( !isWorking, "renderRoot was called recursively. This error is likely caused by a bug in React. Please file an issue." ); isWorking = !0; - root.isReadyForCommit = !1; if ( - root !== nextRoot || expirationTime !== nextRenderExpirationTime || + root !== nextRoot || null === nextUnitOfWork - ) { - for (; -1 < index; ) (valueStack[index] = null), index--; - 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; - changedBitsStack[i] = null; - currentValueStack[i] = null; - stack[i] = null; - } - index$1 = -1; - resetHostContainer(); - nextRoot = root; - nextRenderExpirationTime = expirationTime; - nextUnitOfWork = createWorkInProgress( - nextRoot.current, - null, - expirationTime - ); - } - i = !1; - context = null; - try { - workLoop(isAsync); - } catch (e) { - (i = !0), (context = e); - } - for (; i; ) { - if (didFatal) { - firstUncaughtError = context; - break; - } - expirationTime = nextUnitOfWork; - if (null === expirationTime) didFatal = !0; - else { - var boundary = captureError(expirationTime, context); - 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 { - i = boundary; - context = isAsync; - for (boundary = i; null !== expirationTime; ) { - switch (expirationTime.tag) { - case 2: - popContextProvider(expirationTime); - break; - case 5: - popHostContext(expirationTime); - break; - case 3: - popHostContainer(expirationTime); - break; - case 4: - popHostContainer(expirationTime); - break; - case 13: - popProvider(expirationTime); - } - if ( - expirationTime === boundary || - expirationTime.alternate === boundary - ) - break; - expirationTime = expirationTime["return"]; - } - nextUnitOfWork = performFailedUnitOfWork(i); - workLoop(context); - } catch (e) { - i = !0; - context = e; - continue; - } + ) + resetContextStack(), + (nextRoot = root), + (nextRenderExpirationTime = expirationTime), + (nextUnitOfWork = createWorkInProgress( + nextRoot.current, + null, + nextRenderExpirationTime + )), + (root.pendingCommitExpirationTime = 0); + var didFatal = !1; + do { + try { + if (isAsync) + for (; null !== nextUnitOfWork && !shouldYield(); ) + nextUnitOfWork = performUnitOfWork(nextUnitOfWork); + else + for (; null !== nextUnitOfWork; ) + nextUnitOfWork = performUnitOfWork(nextUnitOfWork); + } catch (thrownValue) { + if (null === nextUnitOfWork) { + didFatal = !0; + onUncaughtError(thrownValue); break; } - } - } - isAsync = firstUncaughtError; - didFatal = isWorking = !1; - firstUncaughtError = null; - null !== isAsync && onUncaughtError(isAsync); - return root.isReadyForCommit ? root.current.alternate : null; - } - function captureError(failedWork, error$jscomp$0) { - var boundary = (ReactCurrentOwner.current = null), - errorBoundaryFound = !1, - willRetry = !1, - errorBoundaryName = null; - if (3 === failedWork.tag) - (boundary = failedWork), isFailedBoundary(failedWork) && (didFatal = !0); - else - for ( - var node = failedWork["return"]; - null !== node && null === boundary; - - ) { - 2 === node.tag - ? "function" === typeof node.stateNode.componentDidCatch && - ((errorBoundaryFound = !0), - (errorBoundaryName = getComponentName(node)), - (boundary = node), - (willRetry = !0)) - : 3 === node.tag && (boundary = node); - if (isFailedBoundary(node)) { - if ( - isUnmounting || - (null !== commitPhaseBoundaries && - (commitPhaseBoundaries.has(node) || - (null !== node.alternate && - commitPhaseBoundaries.has(node.alternate)))) - ) - return null; - boundary = null; - willRetry = !1; + isAsync = nextUnitOfWork; + var returnFiber = isAsync["return"]; + if (null === returnFiber) { + didFatal = !0; + onUncaughtError(thrownValue); + break; } - node = node["return"]; + throwException(returnFiber, isAsync, thrownValue); + nextUnitOfWork = completeUnitOfWork(isAsync); } - if (null !== boundary) { - null === failedBoundaries && (failedBoundaries = new Set()); - failedBoundaries.add(boundary); - var info = ""; - node = failedWork; - do { - a: switch (node.tag) { - case 0: - case 1: + break; + } while (1); + isWorking = !1; + if (didFatal || null !== nextUnitOfWork) return null; + if (isRootReadyForCommit) + return ( + (root.pendingCommitExpirationTime = expirationTime), + root.current.alternate + ); + invariant( + !1, + "Expired work should have completed. This error is likely caused by a bug in React. Please file an issue." + ); + } + function scheduleCapture(sourceFiber, boundaryFiber, value, expirationTime) { + sourceFiber = { + value: value, + source: sourceFiber, + stack: getStackAddendumByWorkInProgressFiber(sourceFiber) + }; + insertUpdateIntoFiber(boundaryFiber, { + expirationTime: expirationTime, + partialState: null, + callback: null, + isReplace: !1, + isForced: !1, + capturedValue: sourceFiber, + next: null + }); + scheduleWork(boundaryFiber, expirationTime); + } + function onCommitPhaseError(fiber$jscomp$0, error) { + a: { + invariant( + !isWorking || isCommitting, + "dispatch: Cannot dispatch during the render phase." + ); + for (var fiber = fiber$jscomp$0["return"]; null !== fiber; ) { + switch (fiber.tag) { case 2: - case 5: - var owner = node._debugOwner, - source = node._debugSource; - var JSCompiler_inline_result = getComponentName(node); - var ownerName = null; - owner && (ownerName = getComponentName(owner)); - owner = source; - JSCompiler_inline_result = - "\n in " + - (JSCompiler_inline_result || "Unknown") + - (owner - ? " (at " + - owner.fileName.replace(/^.*[\\\/]/, "") + - ":" + - owner.lineNumber + - ")" - : ownerName ? " (created by " + ownerName + ")" : ""); + var instance = fiber.stateNode; + if ( + "function" === typeof fiber.type.getDerivedStateFromCatch || + ("function" === typeof instance.componentDidCatch && + !isAlreadyFailedLegacyErrorBoundary(instance)) + ) { + scheduleCapture(fiber$jscomp$0, fiber, error, 1); + fiber$jscomp$0 = void 0; + break a; + } + break; + case 3: + scheduleCapture(fiber$jscomp$0, fiber, error, 1); + fiber$jscomp$0 = void 0; break a; - default: - JSCompiler_inline_result = ""; } - info += JSCompiler_inline_result; - node = node["return"]; - } while (node); - node = info; - failedWork = getComponentName(failedWork); - null === capturedErrors && (capturedErrors = new Map()); - error$jscomp$0 = { - componentName: failedWork, - componentStack: node, - error: error$jscomp$0, - errorBoundary: errorBoundaryFound ? boundary.stateNode : null, - errorBoundaryFound: errorBoundaryFound, - errorBoundaryName: errorBoundaryName, - willRetry: willRetry - }; - capturedErrors.set(boundary, error$jscomp$0); - try { - 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); + fiber = fiber["return"]; } - isCommitting - ? (null === commitPhaseBoundaries && - (commitPhaseBoundaries = new Set()), - commitPhaseBoundaries.add(boundary)) - : scheduleErrorRecovery(boundary); - return boundary; + 3 === fiber$jscomp$0.tag && + scheduleCapture(fiber$jscomp$0, fiber$jscomp$0, error, 1); + fiber$jscomp$0 = void 0; } - null === firstUncaughtError && (firstUncaughtError = error$jscomp$0); - return null; - } - function hasCapturedError(fiber) { - return ( - null !== capturedErrors && - (capturedErrors.has(fiber) || - (null !== fiber.alternate && capturedErrors.has(fiber.alternate))) - ); - } - function isFailedBoundary(fiber) { - return ( - null !== failedBoundaries && - (failedBoundaries.has(fiber) || - (null !== fiber.alternate && failedBoundaries.has(fiber.alternate))) - ); - } - function computeAsyncExpiration() { - return 20 * ((((recalculateCurrentTime() + 100) / 20) | 0) + 1); + return fiber$jscomp$0; } function computeExpirationForFiber(fiber) { fiber = @@ -4900,7 +5196,7 @@ function ReactFiberScheduler(config) { : fiber.mode & 1 ? isBatchingInteractiveUpdates ? 10 * ((((recalculateCurrentTime() + 50) / 10) | 0) + 1) - : computeAsyncExpiration() + : 25 * ((((recalculateCurrentTime() + 500) / 25) | 0) + 1) : 1; isBatchingInteractiveUpdates && (0 === lowestPendingInteractiveExpirationTime || @@ -4909,39 +5205,41 @@ function ReactFiberScheduler(config) { return fiber; } function scheduleWork(fiber, expirationTime) { - return scheduleWorkImpl(fiber, expirationTime, !1); - } - function scheduleWorkImpl(fiber, expirationTime) { - for (; null !== fiber; ) { - if (0 === fiber.expirationTime || fiber.expirationTime > expirationTime) - fiber.expirationTime = expirationTime; - null !== fiber.alternate && - (0 === fiber.alternate.expirationTime || - fiber.alternate.expirationTime > expirationTime) && - (fiber.alternate.expirationTime = expirationTime); - if (null === fiber["return"]) - if (3 === fiber.tag) { - var root = fiber.stateNode; - !isWorking && - root === nextRoot && - expirationTime < nextRenderExpirationTime && - ((nextUnitOfWork = nextRoot = null), - (nextRenderExpirationTime = 0)); - requestWork(root, expirationTime); - !isWorking && - root === nextRoot && - expirationTime < nextRenderExpirationTime && - ((nextUnitOfWork = nextRoot = null), - (nextRenderExpirationTime = 0)); - } else break; - fiber = fiber["return"]; + a: { + for (; null !== fiber; ) { + if (0 === fiber.expirationTime || fiber.expirationTime > expirationTime) + fiber.expirationTime = expirationTime; + null !== fiber.alternate && + (0 === fiber.alternate.expirationTime || + fiber.alternate.expirationTime > expirationTime) && + (fiber.alternate.expirationTime = expirationTime); + if (null === fiber["return"]) + if (3 === fiber.tag) { + var root = fiber.stateNode; + !isWorking && + 0 !== nextRenderExpirationTime && + expirationTime < nextRenderExpirationTime && + resetContextStack(); + (nextRoot === root && isWorking) || + requestWork(root, expirationTime); + nestedUpdateCount > NESTED_UPDATE_LIMIT && + invariant( + !1, + "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." + ); + } else { + expirationTime = void 0; + break a; + } + fiber = fiber["return"]; + } + expirationTime = void 0; } - } - function scheduleErrorRecovery(fiber) { - scheduleWorkImpl(fiber, 1, !0); + return expirationTime; } function recalculateCurrentTime() { - return (mostRecentCurrentTime = (((now() - startTime) / 10) | 0) + 2); + mostRecentCurrentTimeMs = now() - originalStartTimeMs; + return (mostRecentCurrentTime = ((mostRecentCurrentTimeMs / 10) | 0) + 2); } function syncUpdates(fn, a, b, c, d) { var previousExpirationContext = expirationContext; @@ -4957,18 +5255,13 @@ function ReactFiberScheduler(config) { if (expirationTime > callbackExpirationTime) return; cancelDeferredCallback(callbackID); } - var currentMs = now() - startTime; + var currentMs = now() - originalStartTimeMs; callbackExpirationTime = expirationTime; callbackID = scheduleDeferredCallback(performAsyncWork, { timeout: 10 * (expirationTime - 2) - currentMs }); } function requestWork(root, expirationTime) { - nestedUpdateCount > NESTED_UPDATE_LIMIT && - invariant( - !1, - "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." - ); if (null === root.nextScheduledRoot) (root.remainingExpirationTime = expirationTime), null === lastScheduledRoot @@ -5041,7 +5334,8 @@ function ReactFiberScheduler(config) { } previousScheduledRoot = nextFlushedRoot; null !== previousScheduledRoot && - previousScheduledRoot === highestPriorityRoot + previousScheduledRoot === highestPriorityRoot && + 1 === highestPriorityWork ? nestedUpdateCount++ : (nestedUpdateCount = 0); nextFlushedRoot = highestPriorityRoot; @@ -5086,10 +5380,10 @@ function ReactFiberScheduler(config) { scheduleCallbackWithExpiration(nextFlushedExpirationTime); deadline = null; deadlineDidExpire = !1; - nestedUpdateCount = 0; finishRendering(); } function finishRendering() { + nestedUpdateCount = 0; if (null !== completedBatches) { var batches = completedBatches; completedBatches = null; @@ -5154,18 +5448,24 @@ function ReactFiberScheduler(config) { expirationTime.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." ); - expirationTime.isReadyForCommit = !1; + firstBatch = expirationTime.pendingCommitExpirationTime; + invariant( + 0 !== firstBatch, + "Cannot commit an incomplete root. This error is likely caused by a bug in React. Please file an issue." + ); + expirationTime.pendingCommitExpirationTime = 0; + var currentTime = recalculateCurrentTime(); ReactCurrentOwner.current = null; - 1 < finishedWork.effectTag - ? null !== finishedWork.lastEffect - ? ((finishedWork.lastEffect.nextEffect = finishedWork), - (firstBatch = finishedWork.firstEffect)) - : (firstBatch = finishedWork) - : (firstBatch = finishedWork.firstEffect); + if (1 < finishedWork.effectTag) + if (null !== finishedWork.lastEffect) { + finishedWork.lastEffect.nextEffect = finishedWork; + var firstEffect = finishedWork.firstEffect; + } else firstEffect = finishedWork; + else firstEffect = finishedWork.firstEffect; prepareForCommit(expirationTime.containerInfo); - for (nextEffect = firstBatch; null !== nextEffect; ) { + for (nextEffect = firstEffect; null !== nextEffect; ) { var didError = !1, - _error = void 0; + error = void 0; try { for (; null !== nextEffect; ) { var effectTag = nextEffect.effectTag; @@ -5174,7 +5474,7 @@ function ReactFiberScheduler(config) { var current = nextEffect.alternate; null !== current && commitDetachRef(current); } - switch (effectTag & -242) { + switch (effectTag & 14) { case 2: commitPlacement(nextEffect); nextEffect.effectTag &= -3; @@ -5188,65 +5488,46 @@ function ReactFiberScheduler(config) { commitWork(nextEffect.alternate, nextEffect); break; case 8: - (isUnmounting = !0), - commitDeletion(nextEffect), - (isUnmounting = !1); + commitDeletion(nextEffect); } nextEffect = nextEffect.nextEffect; } } catch (e) { - (didError = !0), (_error = e); + (didError = !0), (error = e); } didError && (invariant( null !== nextEffect, "Should have next effect. This error is likely caused by a bug in React. Please file an issue." ), - captureError(nextEffect, _error), + onCommitPhaseError(nextEffect, error), null !== nextEffect && (nextEffect = nextEffect.nextEffect)); } resetAfterCommit(expirationTime.containerInfo); expirationTime.current = finishedWork; - for (nextEffect = firstBatch; null !== nextEffect; ) { + for (nextEffect = firstEffect; null !== nextEffect; ) { effectTag = !1; current = void 0; try { - for (; null !== nextEffect; ) { + for ( + firstEffect = expirationTime, + didError = currentTime, + error = firstBatch; + null !== nextEffect; + + ) { var effectTag$jscomp$0 = nextEffect.effectTag; effectTag$jscomp$0 & 36 && - commitLifeCycles(nextEffect.alternate, nextEffect); + commitLifeCycles( + firstEffect, + nextEffect.alternate, + nextEffect, + didError, + error + ); + effectTag$jscomp$0 & 256 && + commitErrorLogging(nextEffect, onUncaughtError); effectTag$jscomp$0 & 128 && commitAttachRef(nextEffect); - if (effectTag$jscomp$0 & 64) - switch (((firstBatch = nextEffect), - (didError = void 0), - null !== capturedErrors && - ((didError = capturedErrors.get(firstBatch)), - capturedErrors["delete"](firstBatch), - null == didError && - null !== firstBatch.alternate && - ((firstBatch = firstBatch.alternate), - (didError = capturedErrors.get(firstBatch)), - capturedErrors["delete"](firstBatch))), - invariant( - null != didError, - "No error for given unit of work. This error is likely caused by a bug in React. Please file an issue." - ), - firstBatch.tag)) { - case 2: - firstBatch.stateNode.componentDidCatch(didError.error, { - componentStack: didError.componentStack - }); - break; - case 3: - null === firstUncaughtError && - (firstUncaughtError = didError.error); - break; - default: - invariant( - !1, - "Invalid type of work. This error is likely caused by a bug in React. Please file an issue." - ); - } var next = nextEffect.nextEffect; nextEffect.nextEffect = null; nextEffect = next; @@ -5259,20 +5540,13 @@ function ReactFiberScheduler(config) { null !== nextEffect, "Should have next effect. This error is likely caused by a bug in React. Please file an issue." ), - captureError(nextEffect, current), + onCommitPhaseError(nextEffect, current), null !== nextEffect && (nextEffect = nextEffect.nextEffect)); } isWorking = isCommitting = !1; "function" === typeof onCommitRoot && onCommitRoot(finishedWork.stateNode); - commitPhaseBoundaries && - (commitPhaseBoundaries.forEach(scheduleErrorRecovery), - (commitPhaseBoundaries = null)); - null !== firstUncaughtError && - ((finishedWork = firstUncaughtError), - (firstUncaughtError = null), - onUncaughtError(finishedWork)); finishedWork = expirationTime.current.expirationTime; - 0 === finishedWork && (failedBoundaries = capturedErrors = null); + 0 === finishedWork && (legacyErrorBoundariesThatAlreadyFailed = null); root.remainingExpirationTime = finishedWork; } function shouldYield() { @@ -5291,26 +5565,41 @@ function ReactFiberScheduler(config) { } var hostContext = ReactFiberHostContext(config), hydrationContext = ReactFiberHydrationContext(config), - popHostContainer = hostContext.popHostContainer, - popHostContext = hostContext.popHostContext, resetHostContainer = hostContext.resetHostContainer, - _ReactFiberBeginWork = ReactFiberBeginWork( + beginWork = ReactFiberBeginWork( config, hostContext, hydrationContext, scheduleWork, computeExpirationForFiber - ), - beginWork = _ReactFiberBeginWork.beginWork, - beginFailedWork = _ReactFiberBeginWork.beginFailedWork, + ).beginWork, completeWork = ReactFiberCompleteWork(config, hostContext, hydrationContext) .completeWork; - hostContext = ReactFiberCommitWork(config, captureError); + hostContext = ReactFiberUnwindWork( + hostContext, + scheduleWork, + isAlreadyFailedLegacyErrorBoundary + ); + var throwException = hostContext.throwException, + unwindWork = hostContext.unwindWork; + hostContext = ReactFiberCommitWork( + config, + onCommitPhaseError, + scheduleWork, + computeExpirationForFiber, + function(instance) { + null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([instance])) + : legacyErrorBoundariesThatAlreadyFailed.add(instance); + }, + recalculateCurrentTime + ); var commitResetTextContent = hostContext.commitResetTextContent, commitPlacement = hostContext.commitPlacement, commitDeletion = hostContext.commitDeletion, commitWork = hostContext.commitWork, commitLifeCycles = hostContext.commitLifeCycles, + commitErrorLogging = hostContext.commitErrorLogging, commitAttachRef = hostContext.commitAttachRef, commitDetachRef = hostContext.commitDetachRef, now = config.now, @@ -5318,8 +5607,9 @@ function ReactFiberScheduler(config) { cancelDeferredCallback = config.cancelDeferredCallback, prepareForCommit = config.prepareForCommit, resetAfterCommit = config.resetAfterCommit, - startTime = now(), + originalStartTimeMs = now(), mostRecentCurrentTime = 2, + mostRecentCurrentTimeMs = originalStartTimeMs, lastUniqueAsyncExpiration = 0, expirationContext = 0, isWorking = !1, @@ -5327,13 +5617,9 @@ function ReactFiberScheduler(config) { nextRoot = null, nextRenderExpirationTime = 0, nextEffect = null, - capturedErrors = null, - failedBoundaries = null, - commitPhaseBoundaries = null, - firstUncaughtError = null, - didFatal = !1, isCommitting = !1, - isUnmounting = !1, + isRootReadyForCommit = !1, + legacyErrorBoundariesThatAlreadyFailed = null, firstScheduledRoot = null, lastScheduledRoot = null, callbackExpirationTime = 0, @@ -5354,6 +5640,7 @@ function ReactFiberScheduler(config) { nestedUpdateCount = 0, timeHeuristicForUnitOfWork = 1; return { + recalculateCurrentTime: recalculateCurrentTime, computeExpirationForFiber: computeExpirationForFiber, scheduleWork: scheduleWork, requestWork: requestWork, @@ -5414,7 +5701,8 @@ function ReactFiberScheduler(config) { }, deferredUpdates: function(fn) { var previousExpirationContext = expirationContext; - expirationContext = computeAsyncExpiration(); + expirationContext = + 25 * ((((recalculateCurrentTime() + 500) / 25) | 0) + 1); try { return fn(); } finally { @@ -5448,7 +5736,7 @@ function ReactFiberScheduler(config) { (lowestPendingInteractiveExpirationTime = 0)); }, computeUniqueAsyncExpiration: function() { - var result = computeAsyncExpiration(); + var result = 25 * ((((recalculateCurrentTime() + 500) / 25) | 0) + 1); result <= lastUniqueAsyncExpiration && (result = lastUniqueAsyncExpiration + 1); return (lastUniqueAsyncExpiration = result); @@ -5460,10 +5748,11 @@ function ReactFiberReconciler$1(config) { element, container, parentComponent, + currentTime, expirationTime, callback ) { - var current = container.current; + currentTime = container.current; if (parentComponent) { parentComponent = parentComponent._reactInternalFiber; var parentContext; @@ -5495,15 +5784,16 @@ function ReactFiberReconciler$1(config) { ? (container.context = parentComponent) : (container.pendingContext = parentComponent); container = callback; - insertUpdateIntoFiber(current, { + insertUpdateIntoFiber(currentTime, { expirationTime: expirationTime, partialState: { element: element }, callback: void 0 === container ? null : container, isReplace: !1, isForced: !1, + capturedValue: null, next: null }); - scheduleWork(current, expirationTime); + scheduleWork(currentTime, expirationTime); return expirationTime; } function findHostInstance(fiber) { @@ -5512,7 +5802,8 @@ function ReactFiberReconciler$1(config) { } var getPublicInstance = config.getPublicInstance; config = ReactFiberScheduler(config); - var computeExpirationForFiber = config.computeExpirationForFiber, + var recalculateCurrentTime = config.recalculateCurrentTime, + computeExpirationForFiber = config.computeExpirationForFiber, scheduleWork = config.scheduleWork; return { createContainer: function(containerInfo, isAsync, hydrate) { @@ -5521,28 +5812,47 @@ function ReactFiberReconciler$1(config) { current: isAsync, containerInfo: containerInfo, pendingChildren: null, - remainingExpirationTime: 0, - isReadyForCommit: !1, + pendingCommitExpirationTime: 0, finishedWork: null, context: null, pendingContext: null, hydrate: hydrate, + remainingExpirationTime: 0, firstBatch: null, nextScheduledRoot: null }; return (isAsync.stateNode = containerInfo); }, updateContainer: function(element, container, parentComponent, callback) { - var expirationTime = computeExpirationForFiber(container.current); + var current = container.current, + currentTime = recalculateCurrentTime(); + current = computeExpirationForFiber(current); return updateContainerAtExpirationTime( element, container, parentComponent, + currentTime, + current, + callback + ); + }, + updateContainerAtExpirationTime: function( + element, + container, + parentComponent, + expirationTime, + callback + ) { + var currentTime = recalculateCurrentTime(); + return updateContainerAtExpirationTime( + element, + container, + parentComponent, + currentTime, expirationTime, callback ); }, - updateContainerAtExpirationTime: updateContainerAtExpirationTime, flushRoot: config.flushRoot, requestWork: config.requestWork, computeUniqueAsyncExpiration: config.computeUniqueAsyncExpiration, @@ -5973,7 +6283,6 @@ var roots = new Map(), } }, ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin, - ReactGlobalSharedState: ReactGlobalSharedState, ReactNativeComponentTree: ReactNativeComponentTree, ReactNativePropRegistry: ReactNativePropRegistry, TouchHistoryMath: TouchHistoryMath, @@ -5998,7 +6307,7 @@ NativeRenderer.injectIntoDevTools({ findFiberByHostInstance: getInstanceFromTag, getInspectorDataForViewTag: getInspectorDataForViewTag, bundleType: 0, - version: "16.3.0-alpha.0", + version: "16.3.0-alpha.1", rendererPackageName: "react-native-renderer" }); var ReactNativeRenderer$2 = Object.freeze({ default: ReactNativeRenderer }),