From a403e76177b1330dacae4f53ef00250d01117fc9 Mon Sep 17 00:00:00 2001 From: Andrei Shikov Date: Mon, 7 Jun 2021 11:41:29 -0700 Subject: [PATCH] React Native sync for revisions 2d8d133...0eea577 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: - **[0eea57724](https://github.com/facebook/react/commit/0eea57724 )**: Fix typo in comment (accumlated → accumulated) ([#21637](https://github.com/facebook/react/pull/21637)) //// - **[0706162ba](https://github.com/facebook/react/commit/0706162ba )**: Fix typo in comment (environement → environment) ([#21635](https://github.com/facebook/react/pull/21635)) //// - **[9d17b562b](https://github.com/facebook/react/commit/9d17b562b )**: Fix typo in comment (satsify → satisfy) ([#21629](https://github.com/facebook/react/pull/21629)) //// - **[b610fec00](https://github.com/facebook/react/commit/b610fec00 )**: fix comments: expiration time -> lanes ([#21551](https://github.com/facebook/react/pull/21551)) //// - **[cc4d24ab0](https://github.com/facebook/react/commit/cc4d24ab0 )**: [Fizz] Always call flush() if it exists ([#21625](https://github.com/facebook/react/pull/21625)) //// - **[e0d9b2899](https://github.com/facebook/react/commit/e0d9b2899 )**: [Fizz] Minor Fixes for Warning Parity ([#21618](https://github.com/facebook/react/pull/21618)) //// - **[1b7b3592f](https://github.com/facebook/react/commit/1b7b3592f )**: [Fizz] Implement Component Stacks in DEV for warnings ([#21610](https://github.com/facebook/react/pull/21610)) //// - **[39f007489](https://github.com/facebook/react/commit/39f007489 )**: Make enableSuspenseLayoutEffectSemantics static for www ([#21617](https://github.com/facebook/react/pull/21617)) //// - **[8f3794276](https://github.com/facebook/react/commit/8f3794276 )**: Prepare semver (`latest`) releases in CI ([#21615](https://github.com/facebook/react/pull/21615)) //// - **[8b4201535](https://github.com/facebook/react/commit/8b4201535 )**: Devtools: add feature to trigger an error boundary ([#21583](https://github.com/facebook/react/pull/21583)) //// - **[154a8cf32](https://github.com/facebook/react/commit/154a8cf32 )**: Fix reference to wrong variable //// - **[6736a38b9](https://github.com/facebook/react/commit/6736a38b9 )**: Add single source of truth for package versions ([#21608](https://github.com/facebook/react/pull/21608)) //// - **[86715efa2](https://github.com/facebook/react/commit/86715efa2 )**: Resolve the true entry point during tests ([#21505](https://github.com/facebook/react/pull/21505)) //// - **[a8a4742f1](https://github.com/facebook/react/commit/a8a4742f1 )**: Convert ES6/TypeScript/CoffeeScript Tests to createRoot + act ([#21598](https://github.com/facebook/react/pull/21598)) //// - **[1d3558965](https://github.com/facebook/react/commit/1d3558965 )**: Disable deferRenderPhaseUpdateToNextBatch by default ([#21605](https://github.com/facebook/react/pull/21605)) //// - **[a8964649b](https://github.com/facebook/react/commit/a8964649b )**: Delete an unused field ([#21415](https://github.com/facebook/react/pull/21415)) //// - **[76f85b3e5](https://github.com/facebook/react/commit/76f85b3e5 )**: Expose Fizz in stable builds ([#21602](https://github.com/facebook/react/pull/21602)) //// - **[e16d61c30](https://github.com/facebook/react/commit/e16d61c30 )**: [Offscreen] Mount/unmount layout effects ([#21386](https://github.com/facebook/react/pull/21386)) //// - **[63091939b](https://github.com/facebook/react/commit/63091939b )**: OSS feature flag updates ([#21597](https://github.com/facebook/react/pull/21597)) //// - **[efbd69b27](https://github.com/facebook/react/commit/efbd69b27 )**: Define global __WWW__ = true flag during www tests ([#21504](https://github.com/facebook/react/pull/21504)) //// - **[28625c6f4](https://github.com/facebook/react/commit/28625c6f4 )**: Disable strict effects for legacy roots (again) ([#21591](https://github.com/facebook/react/pull/21591)) //// - **[3c2341416](https://github.com/facebook/react/commit/3c2341416 )**: Update jest to v26 ([#21574](https://github.com/facebook/react/pull/21574)) //// - **[0d493dcda](https://github.com/facebook/react/commit/0d493dcda )**: Removed _debugID field from Fiber - Issue #21558 ([#21570](https://github.com/facebook/react/pull/21570)) //// - **[7841d0695](https://github.com/facebook/react/commit/7841d0695 )**: Enable the updater-tracking feature flag in more builds ([#21567](https://github.com/facebook/react/pull/21567)) //// - **[6405efc36](https://github.com/facebook/react/commit/6405efc36 )**: Enabled Profiling feature flags for OSS release ([#21565](https://github.com/facebook/react/pull/21565)) //// Changelog: [General][Changed] - React Native sync for revisions 2d8d133...0eea577 jest_e2e[run_all_tests] Reviewed By: bvaughn Differential Revision: D28932083 fbshipit-source-id: 012c1bfb857ed59d7283334d633f1cce8ec50360 --- Libraries/Renderer/REVISION | 2 +- .../implementations/ReactFabric-dev.fb.js | 7726 +++++++++-------- .../implementations/ReactFabric-dev.js | 7660 ++++++++-------- .../implementations/ReactFabric-prod.fb.js | 270 +- .../implementations/ReactFabric-prod.js | 270 +- .../ReactFabric-profiling.fb.js | 903 +- .../implementations/ReactFabric-profiling.js | 903 +- .../ReactNativeRenderer-dev.fb.js | 7582 ++++++++-------- .../ReactNativeRenderer-dev.js | 7454 ++++++++-------- .../ReactNativeRenderer-prod.fb.js | 258 +- .../ReactNativeRenderer-prod.js | 258 +- .../ReactNativeRenderer-profiling.fb.js | 911 +- .../ReactNativeRenderer-profiling.js | 911 +- 13 files changed, 19270 insertions(+), 15838 deletions(-) diff --git a/Libraries/Renderer/REVISION b/Libraries/Renderer/REVISION index be9f717f7f4e83..4c01c4c4edc328 100644 --- a/Libraries/Renderer/REVISION +++ b/Libraries/Renderer/REVISION @@ -1 +1 @@ -2d8d133e1756ea50a48e615db0c22870e92f4af6 \ No newline at end of file +0eea5772486318c5b2922c8b36680cf4744615d6 \ No newline at end of file diff --git a/Libraries/Renderer/implementations/ReactFabric-dev.fb.js b/Libraries/Renderer/implementations/ReactFabric-dev.fb.js index d43ff6ad0019ab..3cfaa7ae55ae18 100644 --- a/Libraries/Renderer/implementations/ReactFabric-dev.fb.js +++ b/Libraries/Renderer/implementations/ReactFabric-dev.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<<3836d5af681381d070c07bf87c303843>> + * @generated SignedSource<<0962b5cf17d6b982900b0deedd83fda0>> */ 'use strict'; @@ -2844,7 +2844,6 @@ var enableProfilerTimer = true; var enableLazyElements = false; var warnAboutStringRefs = false; var enableNewReconciler = false; -var deferRenderPhaseUpdateToNextBatch = true; var enableLazyContextPropagation = false; // Don't change these two values. They're used by React Dev Tools. @@ -4697,6 +4696,48 @@ function markRootEntangled(root, entangledLanes) { lanes &= ~lane; } } +function addFiberToLanesMap(root, fiber, lanes) { + if (!isDevToolsPresent) { + return; + } + + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; + + while (lanes > 0) { + var index = laneToIndex(lanes); + var lane = 1 << index; + var updaters = pendingUpdatersLaneMap[index]; + updaters.add(fiber); + lanes &= ~lane; + } +} +function movePendingFibersToMemoized(root, lanes) { + if (!isDevToolsPresent) { + return; + } + + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; + var memoizedUpdaters = root.memoizedUpdaters; + + while (lanes > 0) { + var index = laneToIndex(lanes); + var lane = 1 << index; + var updaters = pendingUpdatersLaneMap[index]; + + if (updaters.size > 0) { + updaters.forEach(function(fiber) { + var alternate = fiber.alternate; + + if (alternate === null || !memoizedUpdaters.has(alternate)) { + memoizedUpdaters.add(fiber); + } + }); + updaters.clear(); + } + + lanes &= ~lane; + } +} var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros. Only used on lanes, so assume input is an integer. // Based on: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 @@ -5812,7 +5853,7 @@ var Passive$1 = /* */ 4; -var ReactVersion = "17.0.3-2d8d133e1"; +var ReactVersion = "17.0.3-0eea57724"; var ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig; var NoTransition = 0; @@ -6596,9 +6637,7 @@ function readContext(context) { lastContextDependency = contextItem; currentlyRenderingFiber.dependencies = { lanes: NoLanes, - firstContext: contextItem, - // TODO: This is an old field. Delete it. - responders: null + firstContext: contextItem }; } else { // Append a new context item. @@ -7992,7 +8031,6 @@ function mountClassInstance(workInProgress, ctor, newProps, renderLanes) { var fiberFlags = Update; if ((workInProgress.mode & StrictEffectsMode) !== NoMode) { - // Never double-invoke effects for legacy roots. fiberFlags |= MountLayoutDev; } @@ -8061,7 +8099,6 @@ function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) { var fiberFlags = Update; if ((workInProgress.mode & StrictEffectsMode) !== NoMode) { - // Never double-invoke effects for legacy roots. fiberFlags |= MountLayoutDev; } @@ -8114,7 +8151,6 @@ function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) { var _fiberFlags = Update; if ((workInProgress.mode & StrictEffectsMode) !== NoMode) { - // Never double-invoke effects for legacy roots. _fiberFlags |= MountLayoutDev; } @@ -8127,7 +8163,6 @@ function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) { var _fiberFlags2 = Update; if ((workInProgress.mode & StrictEffectsMode) !== NoMode) { - // Never double-invoke effects for legacy roots. _fiberFlags2 |= MountLayoutDev; } @@ -12268,7 +12303,52 @@ var InvalidNestedHooksDispatcherOnRerenderInDEV = null; var now$1 = Scheduler.unstable_now; var commitTime = 0; +var layoutEffectStartTime = -1; var profilerStartTime = -1; +var passiveEffectStartTime = -1; +/** + * Tracks whether the current update was a nested/cascading update (scheduled from a layout effect). + * + * The overall sequence is: + * 1. render + * 2. commit (and call `onRender`, `onCommit`) + * 3. check for nested updates + * 4. flush passive effects (and call `onPostCommit`) + * + * Nested updates are identified in step 3 above, + * but step 4 still applies to the work that was just committed. + * We use two flags to track nested updates then: + * one tracks whether the upcoming update is a nested update, + * and the other tracks whether the current update was a nested update. + * The first value gets synced to the second at the start of the render phase. + */ + +var currentUpdateIsNested = false; +var nestedUpdateScheduled = false; + +function isCurrentUpdateNested() { + return currentUpdateIsNested; +} + +function markNestedUpdateScheduled() { + { + nestedUpdateScheduled = true; + } +} + +function resetNestedUpdateFlag() { + { + currentUpdateIsNested = false; + nestedUpdateScheduled = false; + } +} + +function syncNestedUpdateFlag() { + { + currentUpdateIsNested = nestedUpdateScheduled; + nestedUpdateScheduled = false; + } +} function getCommitTime() { return commitTime; @@ -12303,6 +12383,77 @@ function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { } } +function recordLayoutEffectDuration(fiber) { + if (layoutEffectStartTime >= 0) { + var elapsedTime = now$1() - layoutEffectStartTime; + layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor + // Or the root (for the DevTools Profiler to read) + + var parentFiber = fiber.return; + + while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.effectDuration += elapsedTime; + return; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.effectDuration += elapsedTime; + return; + } + + parentFiber = parentFiber.return; + } + } +} + +function recordPassiveEffectDuration(fiber) { + if (passiveEffectStartTime >= 0) { + var elapsedTime = now$1() - passiveEffectStartTime; + passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor + // Or the root (for the DevTools Profiler to read) + + var parentFiber = fiber.return; + + while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + + if (root !== null) { + root.passiveEffectDuration += elapsedTime; + } + + return; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + + if (parentStateNode !== null) { + // Detached fibers have their state node cleared out. + // In this case, the return pointer is also cleared out, + // so we won't be able to report the time spent in this Profiler's subtree. + parentStateNode.passiveEffectDuration += elapsedTime; + } + + return; + } + + parentFiber = parentFiber.return; + } + } +} + +function startLayoutEffectTimer() { + layoutEffectStartTime = now$1(); +} + +function startPassiveEffectTimer() { + passiveEffectStartTime = now$1(); +} + function transferActualDuration(fiber) { // Transfer time spent rendering these children so we don't lose it // after we rerender. This is used as a helper in special cases @@ -12315,491 +12466,564 @@ function transferActualDuration(fiber) { } } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; -var didReceiveUpdate = false; -var didWarnAboutBadClass; -var didWarnAboutModulePatternComponent; -var didWarnAboutContextTypeOnFunctionComponent; -var didWarnAboutGetDerivedStateOnFunctionComponent; -var didWarnAboutFunctionRefs; -var didWarnAboutReassigningProps; -var didWarnAboutRevealOrder; -var didWarnAboutTailOptions; - -{ - didWarnAboutBadClass = {}; - didWarnAboutModulePatternComponent = {}; - didWarnAboutContextTypeOnFunctionComponent = {}; - didWarnAboutGetDerivedStateOnFunctionComponent = {}; - didWarnAboutFunctionRefs = {}; - didWarnAboutReassigningProps = false; - didWarnAboutRevealOrder = {}; - didWarnAboutTailOptions = {}; -} - -function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { - if (current === null) { - // If this is a fresh new component that hasn't been rendered yet, we - // won't update its child set by applying minimal side-effects. Instead, - // we will add them all to the child before it gets rendered. That means - // we can optimize this reconciliation pass by not tracking side-effects. - workInProgress.child = mountChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - } else { - // If the current child is the same as the work in progress, it means that - // we haven't yet started any work on these children. Therefore, we use - // the clone algorithm to create a copy of all the current children. - // If we had any progressed work already, that is invalid at this point so - // let's throw it out. - workInProgress.child = reconcileChildFibers( - workInProgress, - current.child, - nextChildren, - renderLanes - ); - } +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: getStackByFiberInDevAndProd(source) + }; } -function forceUnmountCurrentAndReconcile( - current, - workInProgress, - nextChildren, - renderLanes +if ( + !( + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog === + "function" + ) ) { - // This function is fork of reconcileChildren. It's used in cases where we - // want to reconcile without matching against the existing set. This has the - // effect of all current children being unmounted; even if the type and key - // are the same, the old child is unmounted and a new child is created. - // - // To do this, we're going to go through the reconcile algorithm twice. In - // the first pass, we schedule a deletion for all the current children by - // passing null. - workInProgress.child = reconcileChildFibers( - workInProgress, - current.child, - null, - renderLanes - ); // In the second pass, we mount the new children. The trick here is that we - // pass null in place of where we usually pass the current child set. This has - // the effect of remounting all children regardless of whether their - // identities match. + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +} - workInProgress.child = reconcileChildFibers( - workInProgress, - null, - nextChildren, - renderLanes +function showErrorDialog(boundary, errorInfo) { + var capturedError = { + componentStack: errorInfo.stack !== null ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + boundary !== null && boundary.tag === ClassComponent + ? boundary.stateNode + : null + }; + return ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog( + capturedError ); } -function updateForwardRef( - current, - workInProgress, - Component, - nextProps, - renderLanes -) { - // TODO: current can be non-null here even if the component - // hasn't yet mounted. This happens after the first render suspends. - // We'll need to figure out if this is fine or can cause issues. - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; +function logCapturedError(boundary, errorInfo) { + try { + var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. + // This enables renderers like ReactNative to better manage redbox behavior. - if (innerPropTypes) { - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(Component) - ); - } + if (logError === false) { + return; } - } - var render = Component.render; - var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent + var error = errorInfo.value; - var nextChildren; - prepareToReadContext(workInProgress, renderLanes); + if (true) { + var source = errorInfo.source; + var stack = errorInfo.stack; + var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling + // `preventDefault()` in window `error` handler. + // We record this information as an expando on the error. - { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - nextChildren = renderWithHooks( - current, - workInProgress, - render, - nextProps, - ref, - renderLanes - ); + if (error != null && error._suppressLogging) { + if (boundary.tag === ClassComponent) { + // The error is recoverable and was silenced. + // Ignore it and don't print the stack addendum. + // This is handy for testing error boundaries without noise. + return; + } // The error is fatal. Since the silencing might have + // been accidental, we'll surface it anyway. + // However, the browser would have silenced the original error + // so we'll print it first, and then print the stack addendum. - if (workInProgress.mode & StrictLegacyMode) { - disableLogs(); + console["error"](error); // Don't transform to our wrapper + // For a more detailed description of this block, see: + // https://github.com/facebook/react/pull/13384 + } - try { - nextChildren = renderWithHooks( - current, - workInProgress, - render, - nextProps, - ref, - renderLanes - ); - } finally { - reenableLogs(); + var componentName = source ? getComponentNameFromFiber(source) : null; + var componentNameMessage = componentName + ? "The above error occurred in the <" + componentName + "> component:" + : "The above error occurred in one of your React components:"; + var errorBoundaryMessage; + + if (boundary.tag === HostRoot) { + errorBoundaryMessage = + "Consider adding an error boundary to your tree to customize error handling behavior.\n" + + "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries."; + } else { + var errorBoundaryName = + getComponentNameFromFiber(boundary) || "Anonymous"; + errorBoundaryMessage = + "React will try to recreate this component tree from scratch " + + ("using the error boundary you provided, " + errorBoundaryName + "."); } - } - setIsRendering(false); + var combinedMessage = + componentNameMessage + + "\n" + + 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); // Don't transform to our wrapper + } else { + // In production, we print the error directly. + // This will include the message, the JS stack, and anything the browser wants to show. + // We pass the error object instead of custom message so that the browser displays the error natively. + console["error"](error); // Don't transform to our wrapper + } + } catch (e) { + // This method must not throw, or React internal state will get messed up. + // If console.error is overridden, or logCapturedError() shows a dialog that throws, + // we want to report this error outside of the normal stack as a last resort. + // https://github.com/facebook/react/issues/13188 + setTimeout(function() { + throw e; + }); } +} - if (current !== null && !didReceiveUpdate) { - bailoutHooks(current, workInProgress, renderLanes); - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } // React DevTools reads this flag. +var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; +function createRootErrorUpdate(fiber, errorInfo, lane) { + var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. + + update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property + // being called "element". + + update.payload = { + element: null + }; + var error = errorInfo.value; + + update.callback = function() { + onUncaughtError(error); + logCapturedError(fiber, errorInfo); + }; + + return update; } -function updateMemoComponent( - current, - workInProgress, - Component, - nextProps, - updateLanes, - renderLanes -) { - if (current === null) { - var type = Component.type; +function createClassErrorUpdate(fiber, errorInfo, lane) { + var update = createUpdate(NoTimestamp, lane); + update.tag = CaptureUpdate; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ( - isSimpleFunctionComponent(type) && - Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. - Component.defaultProps === undefined - ) { - var resolvedType = type; + if (typeof getDerivedStateFromError === "function") { + var error$1 = errorInfo.value; - { - resolvedType = resolveFunctionForHotReloading(type); - } // If this is a plain function component without default props, - // and with only the default shallow comparison, we upgrade it - // to a SimpleMemoComponent to allow fast path updates. + update.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error$1); + }; + } - workInProgress.tag = SimpleMemoComponent; - workInProgress.type = resolvedType; + var inst = fiber.stateNode; + if (inst !== null && typeof inst.componentDidCatch === "function") { + update.callback = function callback() { { - validateFunctionComponentInDev(workInProgress, type); + markFailedErrorBoundaryForHotReloading(fiber); } - return updateSimpleMemoComponent( - current, - workInProgress, - resolvedType, - nextProps, - updateLanes, - renderLanes - ); - } - - { - var innerPropTypes = type.propTypes; + if (typeof getDerivedStateFromError !== "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 getDerivedStateFromError is + // not defined. + markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined - if (innerPropTypes) { - // Inner memo component props aren't currently validated in createElement. - // We could move it there, but we'd still need this for lazy code path. - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(type) - ); + logCapturedError(fiber, errorInfo); } - } - var child = createFiberFromTypeAndProps( - Component.type, - null, - nextProps, - workInProgress, - workInProgress.mode, - renderLanes - ); - child.ref = workInProgress.ref; - child.return = workInProgress; - workInProgress.child = child; - return child; + var error$1 = errorInfo.value; + var stack = errorInfo.stack; + this.componentDidCatch(error$1, { + componentStack: stack !== null ? stack : "" + }); + + { + if (typeof getDerivedStateFromError !== "function") { + // If componentDidCatch is the only error boundary method defined, + // then it needs to call setState to recover from errors. + // If no state update is scheduled then the boundary will swallow the error. + if (!includesSomeLane(fiber.lanes, SyncLane)) { + error( + "%s: Error boundaries should implement getDerivedStateFromError(). " + + "In that method, return a state update to display an error message or fallback UI.", + getComponentNameFromFiber(fiber) || "Unknown" + ); + } + } + } + }; + } else { + update.callback = function() { + markFailedErrorBoundaryForHotReloading(fiber); + }; } - { - var _type = Component.type; - var _innerPropTypes = _type.propTypes; + return update; +} - if (_innerPropTypes) { - // Inner memo component props aren't currently validated in createElement. - // We could move it there, but we'd still need this for lazy code path. - checkPropTypes( - _innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(_type) - ); - } - } +function attachPingListener(root, wakeable, lanes) { + // Attach a listener to the promise to "ping" the root and retry. But only if + // one does not already exist for the lanes we're currently rendering (which + // acts like a "thread ID" here). + var pingCache = root.pingCache; + var threadIDs; - var currentChild = current.child; // This is always exactly one child + if (pingCache === null) { + pingCache = root.pingCache = new PossiblyWeakMap$1(); + threadIDs = new Set(); + pingCache.set(wakeable, threadIDs); + } else { + threadIDs = pingCache.get(wakeable); - if (!includesSomeLane(updateLanes, renderLanes)) { - // This will be the props with resolved defaultProps, - // unlike current.memoizedProps which will be the unresolved ones. - var prevProps = currentChild.memoizedProps; // Default to shallow comparison + if (threadIDs === undefined) { + threadIDs = new Set(); + pingCache.set(wakeable, threadIDs); + } + } - var compare = Component.compare; - compare = compare !== null ? compare : shallowEqual; + if (!threadIDs.has(lanes)) { + // Memoize using the thread ID to prevent redundant listeners. + threadIDs.add(lanes); + var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); - if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + { + if (isDevToolsPresent) { + // If we have pending work still, restore the original updaters + restorePendingUpdaters(root, lanes); + } } - } // React DevTools reads this flag. - workInProgress.flags |= PerformedWork; - var newChild = createWorkInProgress(currentChild, nextProps); - newChild.ref = workInProgress.ref; - newChild.return = workInProgress; - workInProgress.child = newChild; - return newChild; + wakeable.then(ping, ping); + } } -function updateSimpleMemoComponent( - current, - workInProgress, - Component, - nextProps, - updateLanes, - renderLanes +function throwException( + root, + returnFiber, + sourceFiber, + value, + rootRenderLanes ) { - // TODO: current can be non-null here even if the component - // hasn't yet mounted. This happens when the inner render suspends. - // We'll need to figure out if this is fine or can cause issues. + // The source fiber did not complete. + sourceFiber.flags |= Incomplete; + { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var outerMemoType = workInProgress.elementType; - - if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { - // We warn when you define propTypes on lazy() - // so let's just skip over it to find memo() outer wrapper. - // Inner props for memo are validated later. - var lazyComponent = outerMemoType; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - - try { - outerMemoType = init(payload); - } catch (x) { - outerMemoType = null; - } // Inner propTypes will be validated in the function component path. - - var outerPropTypes = outerMemoType && outerMemoType.propTypes; - - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - nextProps, // Resolved (SimpleMemoComponent has no defaultProps) - "prop", - getComponentNameFromType(outerMemoType) - ); - } - } + if (isDevToolsPresent) { + // If we have pending work still, restore the original updaters + restorePendingUpdaters(root, rootRenderLanes); } } - if (current !== null) { - var prevProps = current.memoizedProps; + if ( + value !== null && + typeof value === "object" && + typeof value.then === "function" + ) { + var wakeable = value; + // A legacy mode Suspense quirk, only relevant to hook components. + + var tag = sourceFiber.tag; if ( - shallowEqual(prevProps, nextProps) && - current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload. - workInProgress.type === current.type + (sourceFiber.mode & ConcurrentMode) === NoMode && + (tag === FunctionComponent || + tag === ForwardRef || + tag === SimpleMemoComponent) ) { - didReceiveUpdate = false; + var currentSource = sourceFiber.alternate; - if (!includesSomeLane(renderLanes, updateLanes)) { - // The pending lanes were cleared at the beginning of beginWork. We're - // about to bail out, but there might be other lanes that weren't - // included in the current render. Usually, the priority level of the - // remaining updates is accumlated during the evaluation of the - // component (i.e. when processing the update queue). But since since - // we're bailing out early *without* evaluating the component, we need - // to account for it here, too. Reset to the value of the current fiber. - // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, - // because a MemoComponent fiber does not have hooks or an update queue; - // rather, it wraps around an inner component, which may or may not - // contains hooks. - // TODO: Move the reset at in beginWork out of the common path so that - // this is no longer necessary. - workInProgress.lanes = current.lanes; - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); - } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { - // This is a special case that only exists for legacy mode. - // See https://github.com/facebook/react/pull/19216. - didReceiveUpdate = true; + if (currentSource) { + sourceFiber.updateQueue = currentSource.updateQueue; + sourceFiber.memoizedState = currentSource.memoizedState; + sourceFiber.lanes = currentSource.lanes; + } else { + sourceFiber.updateQueue = null; + sourceFiber.memoizedState = null; } } - } - return updateFunctionComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes - ); -} - -function updateOffscreenComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; - var nextChildren = nextProps.children; - var prevState = current !== null ? current.memoizedState : null; // If this is not null, this is a cache pool that was carried over from the - // previous render. We will push this to the cache pool context so that we can - // resume in-flight requests. + var hasInvisibleParentBoundary = hasSuspenseContext( + suspenseStackCursor.current, + InvisibleParentSuspenseContext + ); // Schedule the nearest Suspense to re-render the timed out view. - var spawnedCachePool = null; + var _workInProgress = returnFiber; - if ( - nextProps.mode === "hidden" || - nextProps.mode === "unstable-defer-without-hiding" - ) { - // Rendering a hidden tree. - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - // In legacy sync mode, don't defer the subtree. Render it now. - var nextState = { - baseLanes: NoLanes, - cachePool: null - }; - workInProgress.memoizedState = nextState; - pushRenderLanes(workInProgress, renderLanes); - } else if (!includesSomeLane(renderLanes, OffscreenLane)) { - // We're hidden, and we're not rendering at Offscreen. We will bail out - // and resume this tree later. - var nextBaseLanes; + do { + if ( + _workInProgress.tag === SuspenseComponent && + shouldCaptureSuspense(_workInProgress, hasInvisibleParentBoundary) + ) { + // Found the nearest boundary. + // Stash the promise on the boundary fiber. If the boundary times out, we'll + // attach another listener to flip the boundary back to its normal state. + var wakeables = _workInProgress.updateQueue; - if (prevState !== null) { - var prevBaseLanes = prevState.baseLanes; - nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); - } else { - nextBaseLanes = renderLanes; - } // Schedule this fiber to re-render at offscreen priority. Then bailout. + if (wakeables === null) { + var updateQueue = new Set(); + updateQueue.add(wakeable); + _workInProgress.updateQueue = updateQueue; + } else { + wakeables.add(wakeable); + } // If the boundary is in legacy mode, we should *not* + // suspend the commit. Pretend as if the suspended component rendered + // null and keep rendering. In the commit phase, we'll schedule a + // subsequent synchronous update to re-render the Suspense. + // + // Note: It doesn't matter whether the component that suspended was + // inside a concurrent mode tree. If the Suspense is outside of it, we + // should *not* suspend the commit. + // + // If the suspense boundary suspended itself suspended, we don't have to + // do this trick because nothing was partially started. We can just + // directly do a second pass over the fallback in this render and + // pretend we meant to render that directly. - workInProgress.lanes = workInProgress.childLanes = laneToLanes( - OffscreenLane - ); - var _nextState = { - baseLanes: nextBaseLanes, - cachePool: spawnedCachePool - }; - workInProgress.memoizedState = _nextState; - workInProgress.updateQueue = null; // We're about to bail out, but we need to push this to the stack anyway - // to avoid a push/pop misalignment. + if ( + (_workInProgress.mode & ConcurrentMode) === NoMode && + _workInProgress !== returnFiber + ) { + _workInProgress.flags |= DidCapture; + sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. + // But we shouldn't call any lifecycle methods or callbacks. Remove + // all lifecycle effect tags. - pushRenderLanes(workInProgress, nextBaseLanes); + sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); - return null; - } else { - var _nextState2 = { - baseLanes: NoLanes, - cachePool: null - }; - workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. + if (sourceFiber.tag === ClassComponent) { + var _currentSourceFiber = sourceFiber.alternate; - var subtreeRenderLanes = - prevState !== null ? prevState.baseLanes : renderLanes; - pushRenderLanes(workInProgress, subtreeRenderLanes); - } - } else { - // Rendering a visible tree. - var _subtreeRenderLanes; + if (_currentSourceFiber === null) { + // This is a new mount. Change the tag so it's not mistaken for a + // completed class component. For example, we should not call + // componentWillUnmount if it is deleted. + sourceFiber.tag = IncompleteClassComponent; + } else { + // When we try rendering again, we should not reuse the current fiber, + // since it's known to be in an inconsistent state. Use a force update to + // prevent a bail out. + var update = createUpdate(NoTimestamp, SyncLane); + update.tag = ForceUpdate; + enqueueUpdate(sourceFiber, update); + } + } // The source fiber did not complete. Mark it with Sync priority to + // indicate that it still has pending work. - if (prevState !== null) { - // We're going from hidden -> visible. - _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); + sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); // Exit without suspending. - workInProgress.memoizedState = null; - } else { - // We weren't previously hidden, and we still aren't, so there's nothing - // special to do. Need to push to the stack regardless, though, to avoid - // a push/pop misalignment. - _subtreeRenderLanes = renderLanes; - } + return; + } // Confirmed that the boundary is in a concurrent mode tree. Continue + // with the normal suspend path. + // + // After this we'll use a set of heuristics to determine whether this + // render pass will run to completion or restart or "suspend" the commit. + // The actual logic for this is spread out in different places. + // + // This first principle is that if we're going to suspend when we complete + // a root, then we should also restart if we get an update or ping that + // might unsuspend it, and vice versa. The only reason to suspend is + // because you think you might want to restart before committing. However, + // it doesn't make sense to restart only while in the period we're suspended. + // + // Restarting too aggressively is also not good because it starves out any + // intermediate loading state. So we use heuristics to determine when. + // Suspense Heuristics + // + // If nothing threw a Promise or all the same fallbacks are already showing, + // then don't suspend/restart. + // + // If this is an initial render of a new tree of Suspense boundaries and + // those trigger a fallback, then don't suspend/restart. We want to ensure + // that we can show the initial loading state as quickly as possible. + // + // If we hit a "Delayed" case, such as when we'd switch from content back into + // a fallback, then we should always suspend/restart. Transitions apply + // to this case. If none is defined, JND is used instead. + // + // If we're already showing a fallback and it gets "retried", allowing us to show + // another level, but there's still an inner boundary that would show a fallback, + // then we suspend/restart for 500ms since the last time we showed a fallback + // anywhere in the tree. This effectively throttles progressive loading into a + // consistent train of commits. This also gives us an opportunity to restart to + // get to the completed state slightly earlier. + // + // If there's ambiguity due to batching it's resolved in preference of: + // 1) "delayed", 2) "initial render", 3) "retry". + // + // We want to ensure that a "busy" state doesn't get force committed. We want to + // ensure that new initial loading states can commit as soon as possible. - pushRenderLanes(workInProgress, _subtreeRenderLanes); - } + attachPingListener(root, wakeable, rootRenderLanes); + _workInProgress.flags |= ShouldCapture; + _workInProgress.lanes = rootRenderLanes; + return; + } // This boundary already captured during this render. Continue to the next + // boundary. - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} // Note: These happen to have identical begin phases, for now. We shouldn't hold -// ourselves to this constraint, though. If the behavior diverges, we should -// fork the function. + _workInProgress = _workInProgress.return; + } while (_workInProgress !== null); // No boundary was found. Fallthrough to error mode. + // TODO: Use invariant so the message is stripped in prod? -var updateLegacyHiddenComponent = updateOffscreenComponent; + value = new Error( + (getComponentNameFromFiber(sourceFiber) || "A React component") + + " suspended while rendering, but no fallback UI was specified.\n" + + "\n" + + "Add a component higher in the tree to " + + "provide a loading indicator or placeholder to display." + ); + } // We didn't find a boundary that could handle this type of exception. Start + // over and traverse parent path again, this time treating the exception + // as an error. -function updateFragment(current, workInProgress, renderLanes) { - var nextChildren = workInProgress.pendingProps; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; + renderDidError(); + value = createCapturedValue(value, sourceFiber); + var workInProgress = returnFiber; + + do { + switch (workInProgress.tag) { + case HostRoot: { + var _errorInfo = value; + workInProgress.flags |= ShouldCapture; + var lane = pickArbitraryLane(rootRenderLanes); + workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); + + var _update = createRootErrorUpdate(workInProgress, _errorInfo, lane); + + enqueueCapturedUpdate(workInProgress, _update); + return; + } + + case ClassComponent: + // Capture and retry + var errorInfo = value; + var ctor = workInProgress.type; + var instance = workInProgress.stateNode; + + if ( + (workInProgress.flags & DidCapture) === NoFlags && + (typeof ctor.getDerivedStateFromError === "function" || + (instance !== null && + typeof instance.componentDidCatch === "function" && + !isAlreadyFailedLegacyErrorBoundary(instance))) + ) { + workInProgress.flags |= ShouldCapture; + + var _lane = pickArbitraryLane(rootRenderLanes); + + workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state + + var _update2 = createClassErrorUpdate( + workInProgress, + errorInfo, + _lane + ); + + enqueueCapturedUpdate(workInProgress, _update2); + return; + } + + break; + } + + workInProgress = workInProgress.return; + } while (workInProgress !== null); } -function updateMode(current, workInProgress, renderLanes) { - var nextChildren = workInProgress.pendingProps.children; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var didReceiveUpdate = false; +var didWarnAboutBadClass; +var didWarnAboutModulePatternComponent; +var didWarnAboutContextTypeOnFunctionComponent; +var didWarnAboutGetDerivedStateOnFunctionComponent; +var didWarnAboutFunctionRefs; +var didWarnAboutReassigningProps; +var didWarnAboutRevealOrder; +var didWarnAboutTailOptions; + +{ + didWarnAboutBadClass = {}; + didWarnAboutModulePatternComponent = {}; + didWarnAboutContextTypeOnFunctionComponent = {}; + didWarnAboutGetDerivedStateOnFunctionComponent = {}; + didWarnAboutFunctionRefs = {}; + didWarnAboutReassigningProps = false; + didWarnAboutRevealOrder = {}; + didWarnAboutTailOptions = {}; } -function updateProfiler(current, workInProgress, renderLanes) { - { - workInProgress.flags |= Update; +function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { + if (current === null) { + // If this is a fresh new component that hasn't been rendered yet, we + // won't update its child set by applying minimal side-effects. Instead, + // we will add them all to the child before it gets rendered. That means + // we can optimize this reconciliation pass by not tracking side-effects. + workInProgress.child = mountChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); + } else { + // If the current child is the same as the work in progress, it means that + // we haven't yet started any work on these children. Therefore, we use + // the clone algorithm to create a copy of all the current children. + // If we had any progressed work already, that is invalid at this point so + // let's throw it out. + workInProgress.child = reconcileChildFibers( + workInProgress, + current.child, + nextChildren, + renderLanes + ); } - - var nextProps = workInProgress.pendingProps; - var nextChildren = nextProps.children; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; } -function markRef(current, workInProgress) { - var ref = workInProgress.ref; +function forceUnmountCurrentAndReconcile( + current, + workInProgress, + nextChildren, + renderLanes +) { + // This function is fork of reconcileChildren. It's used in cases where we + // want to reconcile without matching against the existing set. This has the + // effect of all current children being unmounted; even if the type and key + // are the same, the old child is unmounted and a new child is created. + // + // To do this, we're going to go through the reconcile algorithm twice. In + // the first pass, we schedule a deletion for all the current children by + // passing null. + workInProgress.child = reconcileChildFibers( + workInProgress, + current.child, + null, + renderLanes + ); // In the second pass, we mount the new children. The trick here is that we + // pass null in place of where we usually pass the current child set. This has + // the effect of remounting all children regardless of whether their + // identities match. - if ( - (current === null && ref !== null) || - (current !== null && current.ref !== ref) - ) { - // Schedule a Ref effect - workInProgress.flags |= Ref; - } + workInProgress.child = reconcileChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); } -function updateFunctionComponent( +function updateForwardRef( current, workInProgress, Component, nextProps, renderLanes ) { + // TODO: current can be non-null here even if the component + // hasn't yet mounted. This happens after the first render suspends. + // We'll need to figure out if this is fine or can cause issues. { if (workInProgress.type !== workInProgress.elementType) { // Lazy component props can't be validated in createElement @@ -12817,12 +13041,8 @@ function updateFunctionComponent( } } - var context; - - { - var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); - context = getMaskedContext(workInProgress, unmaskedContext); - } + var render = Component.render; + var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent var nextChildren; prepareToReadContext(workInProgress, renderLanes); @@ -12833,9 +13053,9 @@ function updateFunctionComponent( nextChildren = renderWithHooks( current, workInProgress, - Component, + render, nextProps, - context, + ref, renderLanes ); @@ -12846,9 +13066,9 @@ function updateFunctionComponent( nextChildren = renderWithHooks( current, workInProgress, - Component, + render, nextProps, - context, + ref, renderLanes ); } finally { @@ -12869,442 +13089,470 @@ function updateFunctionComponent( return workInProgress.child; } -function updateClassComponent( +function updateMemoComponent( current, workInProgress, Component, nextProps, + updateLanes, renderLanes ) { - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; + if (current === null) { + var type = Component.type; + + if ( + isSimpleFunctionComponent(type) && + Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. + Component.defaultProps === undefined + ) { + var resolvedType = type; + + { + resolvedType = resolveFunctionForHotReloading(type); + } // If this is a plain function component without default props, + // and with only the default shallow comparison, we upgrade it + // to a SimpleMemoComponent to allow fast path updates. + + workInProgress.tag = SimpleMemoComponent; + workInProgress.type = resolvedType; + + { + validateFunctionComponentInDev(workInProgress, type); + } + + return updateSimpleMemoComponent( + current, + workInProgress, + resolvedType, + nextProps, + updateLanes, + renderLanes + ); + } + + { + var innerPropTypes = type.propTypes; if (innerPropTypes) { + // Inner memo component props aren't currently validated in createElement. + // We could move it there, but we'd still need this for lazy code path. checkPropTypes( innerPropTypes, nextProps, // Resolved props "prop", - getComponentNameFromType(Component) + getComponentNameFromType(type) ); } } - } // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. - - var hasContext; - - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); - } else { - hasContext = false; - } - prepareToReadContext(workInProgress, renderLanes); - var instance = workInProgress.stateNode; - var shouldUpdate; - - if (instance === null) { - if (current !== null) { - // A class component without an instance only mounts if it suspended - // inside a non-concurrent tree, in an inconsistent state. We want to - // treat it like a new mount, even though an empty version of it already - // committed. Disconnect the alternate pointers. - current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - - workInProgress.flags |= Placement; - } // In the initial pass we might need to construct the instance. - - constructClassInstance(workInProgress, Component, nextProps); - mountClassInstance(workInProgress, Component, nextProps, renderLanes); - shouldUpdate = true; - } else if (current === null) { - // In a resume, we'll already have an instance we can reuse. - shouldUpdate = resumeMountClassInstance( - workInProgress, - Component, + var child = createFiberFromTypeAndProps( + Component.type, + null, nextProps, - renderLanes - ); - } else { - shouldUpdate = updateClassInstance( - current, workInProgress, - Component, - nextProps, + workInProgress.mode, renderLanes ); + child.ref = workInProgress.ref; + child.return = workInProgress; + workInProgress.child = child; + return child; } - var nextUnitOfWork = finishClassComponent( - current, - workInProgress, - Component, - shouldUpdate, - hasContext, - renderLanes - ); - { - var inst = workInProgress.stateNode; - - if (shouldUpdate && inst.props !== nextProps) { - if (!didWarnAboutReassigningProps) { - error( - "It looks like %s is reassigning its own `this.props` while rendering. " + - "This is not supported and can lead to confusing bugs.", - getComponentNameFromFiber(workInProgress) || "a component" - ); - } + var _type = Component.type; + var _innerPropTypes = _type.propTypes; - didWarnAboutReassigningProps = true; + if (_innerPropTypes) { + // Inner memo component props aren't currently validated in createElement. + // We could move it there, but we'd still need this for lazy code path. + checkPropTypes( + _innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(_type) + ); } } - return nextUnitOfWork; + var currentChild = current.child; // This is always exactly one child + + if (!includesSomeLane(updateLanes, renderLanes)) { + // This will be the props with resolved defaultProps, + // unlike current.memoizedProps which will be the unresolved ones. + var prevProps = currentChild.memoizedProps; // Default to shallow comparison + + var compare = Component.compare; + compare = compare !== null ? compare : shallowEqual; + + if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } + } // React DevTools reads this flag. + + workInProgress.flags |= PerformedWork; + var newChild = createWorkInProgress(currentChild, nextProps); + newChild.ref = workInProgress.ref; + newChild.return = workInProgress; + workInProgress.child = newChild; + return newChild; } -function finishClassComponent( +function updateSimpleMemoComponent( current, workInProgress, Component, - shouldUpdate, - hasContext, + nextProps, + updateLanes, renderLanes ) { - // Refs should update even if shouldComponentUpdate returns false - markRef(current, workInProgress); - var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; - - if (!shouldUpdate && !didCaptureError) { - // Context providers should defer to sCU for rendering - if (hasContext) { - invalidateContextProvider(workInProgress, Component, false); - } - - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } - - var instance = workInProgress.stateNode; // Rerender - - ReactCurrentOwner$1.current = workInProgress; - var nextChildren; + // TODO: current can be non-null here even if the component + // hasn't yet mounted. This happens when the inner render suspends. + // We'll need to figure out if this is fine or can cause issues. + { + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var outerMemoType = workInProgress.elementType; - if ( - didCaptureError && - typeof Component.getDerivedStateFromError !== "function" - ) { - // If we captured an error, but getDerivedStateFromError 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; + if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { + // We warn when you define propTypes on lazy() + // so let's just skip over it to find memo() outer wrapper. + // Inner props for memo are validated later. + var lazyComponent = outerMemoType; + var payload = lazyComponent._payload; + var init = lazyComponent._init; - { - stopProfilerTimerIfRunning(); - } - } else { - { - setIsRendering(true); - nextChildren = instance.render(); + try { + outerMemoType = init(payload); + } catch (x) { + outerMemoType = null; + } // Inner propTypes will be validated in the function component path. - if (workInProgress.mode & StrictLegacyMode) { - disableLogs(); + var outerPropTypes = outerMemoType && outerMemoType.propTypes; - try { - instance.render(); - } finally { - reenableLogs(); + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + nextProps, // Resolved (SimpleMemoComponent has no defaultProps) + "prop", + getComponentNameFromType(outerMemoType) + ); } } - - setIsRendering(false); } - } // React DevTools reads this flag. - - workInProgress.flags |= PerformedWork; - - if (current !== null && didCaptureError) { - // If we're recovering from an error, reconcile without reusing any of - // the existing children. Conceptually, the normal children and the children - // that are shown on error are two different sets, so we shouldn't reuse - // normal children even if their identities match. - forceUnmountCurrentAndReconcile( - current, - workInProgress, - nextChildren, - renderLanes - ); - } else { - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - } // Memoize state using the values we just used to render. - // TODO: Restructure so we never read values from the instance. - - workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. - - if (hasContext) { - invalidateContextProvider(workInProgress, Component, true); } - return workInProgress.child; -} + if (current !== null) { + var prevProps = current.memoizedProps; -function pushHostRootContext(workInProgress) { - var root = workInProgress.stateNode; + if ( + shallowEqual(prevProps, nextProps) && + current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload. + workInProgress.type === current.type + ) { + didReceiveUpdate = false; - if (root.pendingContext) { - pushTopLevelContextObject( - workInProgress, - root.pendingContext, - root.pendingContext !== root.context - ); - } else if (root.context) { - // Should always be set - pushTopLevelContextObject(workInProgress, root.context, false); + if (!includesSomeLane(renderLanes, updateLanes)) { + // The pending lanes were cleared at the beginning of beginWork. We're + // about to bail out, but there might be other lanes that weren't + // included in the current render. Usually, the priority level of the + // remaining updates is accumulated during the evaluation of the + // component (i.e. when processing the update queue). But since since + // we're bailing out early *without* evaluating the component, we need + // to account for it here, too. Reset to the value of the current fiber. + // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, + // because a MemoComponent fiber does not have hooks or an update queue; + // rather, it wraps around an inner component, which may or may not + // contains hooks. + // TODO: Move the reset at in beginWork out of the common path so that + // this is no longer necessary. + workInProgress.lanes = current.lanes; + return bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); + } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { + // This is a special case that only exists for legacy mode. + // See https://github.com/facebook/react/pull/19216. + didReceiveUpdate = true; + } + } } - pushHostContainer(workInProgress, root.containerInfo); + return updateFunctionComponent( + current, + workInProgress, + Component, + nextProps, + renderLanes + ); } -function updateHostRoot(current, workInProgress, renderLanes) { - pushHostRootContext(workInProgress); - var updateQueue = workInProgress.updateQueue; +function updateOffscreenComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; + var nextChildren = nextProps.children; + var prevState = current !== null ? current.memoizedState : null; // If this is not null, this is a cache pool that was carried over from the + // previous render. We will push this to the cache pool context so that we can + // resume in-flight requests. - if (!(current !== null && updateQueue !== null)) { - throw Error( - "If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue." - ); - } + var spawnedCachePool = null; - var nextProps = workInProgress.pendingProps; - var prevState = workInProgress.memoizedState; - var prevChildren = prevState.element; - cloneUpdateQueue(current, workInProgress); - processUpdateQueue(workInProgress, nextProps, null, renderLanes); - var nextState = workInProgress.memoizedState; - var root = workInProgress.stateNode; - // being called "element". + if ( + nextProps.mode === "hidden" || + nextProps.mode === "unstable-defer-without-hiding" + ) { + // Rendering a hidden tree. + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + // In legacy sync mode, don't defer the subtree. Render it now. + var nextState = { + baseLanes: NoLanes, + cachePool: null + }; + workInProgress.memoizedState = nextState; + pushRenderLanes(workInProgress, renderLanes); + } else if (!includesSomeLane(renderLanes, OffscreenLane)) { + // We're hidden, and we're not rendering at Offscreen. We will bail out + // and resume this tree later. + var nextBaseLanes; - var nextChildren = nextState.element; + if (prevState !== null) { + var prevBaseLanes = prevState.baseLanes; + nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); + } else { + nextBaseLanes = renderLanes; + } // Schedule this fiber to re-render at offscreen priority. Then bailout. - if (nextChildren === prevChildren) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } + workInProgress.lanes = workInProgress.childLanes = laneToLanes( + OffscreenLane + ); + var _nextState = { + baseLanes: nextBaseLanes, + cachePool: spawnedCachePool + }; + workInProgress.memoizedState = _nextState; + workInProgress.updateQueue = null; // We're about to bail out, but we need to push this to the stack anyway + // to avoid a push/pop misalignment. - if (root.hydrate && enterHydrationState()) { - var child = mountChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - workInProgress.child = child; - var node = child; + pushRenderLanes(workInProgress, nextBaseLanes); - while (node) { - // Mark each child as hydrating. This is a fast path to know whether this - // tree is part of a hydrating tree. This is used to determine if a child - // node has fully mounted yet, and for scheduling event replaying. - // Conceptually this is similar to Placement in that a new subtree is - // inserted into the React tree here. It just happens to not need DOM - // mutations because it already exists. - node.flags = (node.flags & ~Placement) | Hydrating; - node = node.sibling; + return null; + } else { + var _nextState2 = { + baseLanes: NoLanes, + cachePool: null + }; + workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. + + var subtreeRenderLanes = + prevState !== null ? prevState.baseLanes : renderLanes; + pushRenderLanes(workInProgress, subtreeRenderLanes); } } else { - // Otherwise reset hydration state in case we aborted and resumed another - // root. - reconcileChildren(current, workInProgress, nextChildren, renderLanes); + // Rendering a visible tree. + var _subtreeRenderLanes; + + if (prevState !== null) { + // We're going from hidden -> visible. + _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); + + workInProgress.memoizedState = null; + } else { + // We weren't previously hidden, and we still aren't, so there's nothing + // special to do. Need to push to the stack regardless, though, to avoid + // a push/pop misalignment. + _subtreeRenderLanes = renderLanes; + } + + pushRenderLanes(workInProgress, _subtreeRenderLanes); } + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} // Note: These happen to have identical begin phases, for now. We shouldn't hold +// ourselves to this constraint, though. If the behavior diverges, we should +// fork the function. + +var updateLegacyHiddenComponent = updateOffscreenComponent; + +function updateFragment(current, workInProgress, renderLanes) { + var nextChildren = workInProgress.pendingProps; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } -function updateHostComponent(current, workInProgress, renderLanes) { - pushHostContext(workInProgress); +function updateMode(current, workInProgress, renderLanes) { + var nextChildren = workInProgress.pendingProps.children; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} - var type = workInProgress.type; - var nextProps = workInProgress.pendingProps; - var prevProps = current !== null ? current.memoizedProps : null; - var nextChildren = nextProps.children; +function updateProfiler(current, workInProgress, renderLanes) { + { + workInProgress.flags |= Update; - if (prevProps !== null && shouldSetTextContent()) { - // If we're switching from a direct text child to a normal child, or to - // empty, we need to schedule the text content to be reset. - workInProgress.flags |= ContentReset; + { + // Reset effect durations for the next eventual effect phase. + // These are reset during render to allow the DevTools commit hook a chance to read them, + var stateNode = workInProgress.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; + } } - markRef(current, workInProgress); + var nextProps = workInProgress.pendingProps; + var nextChildren = nextProps.children; reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } -function updateHostText(current, workInProgress) { - // immediately after. +function markRef(current, workInProgress) { + var ref = workInProgress.ref; - return null; + if ( + (current === null && ref !== null) || + (current !== null && current.ref !== ref) + ) { + // Schedule a Ref effect + workInProgress.flags |= Ref; + } } -function mountLazyComponent( - _current, +function updateFunctionComponent( + current, workInProgress, - elementType, - updateLanes, + Component, + nextProps, renderLanes ) { - if (_current !== null) { - // A lazy component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + { + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; - workInProgress.flags |= Placement; + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) + ); + } + } } - var props = workInProgress.pendingProps; - var lazyComponent = elementType; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - var Component = init(payload); // Store the unwrapped component in the type. + var context; - workInProgress.type = Component; - var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); - var resolvedProps = resolveDefaultProps(Component, props); - var child; + { + var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); + context = getMaskedContext(workInProgress, unmaskedContext); + } - switch (resolvedTag) { - case FunctionComponent: { - { - validateFunctionComponentInDev(workInProgress, Component); - workInProgress.type = Component = resolveFunctionForHotReloading( - Component - ); - } + var nextChildren; + prepareToReadContext(workInProgress, renderLanes); - child = updateFunctionComponent( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } + { + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + nextChildren = renderWithHooks( + current, + workInProgress, + Component, + nextProps, + context, + renderLanes + ); - case ClassComponent: { - { - workInProgress.type = Component = resolveClassForHotReloading( - Component + if (workInProgress.mode & StrictLegacyMode) { + disableLogs(); + + try { + nextChildren = renderWithHooks( + current, + workInProgress, + Component, + nextProps, + context, + renderLanes ); + } finally { + reenableLogs(); } - - child = updateClassComponent( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; } - case ForwardRef: { - { - workInProgress.type = Component = resolveForwardRefForHotReloading( - Component - ); - } + setIsRendering(false); + } - child = updateForwardRef( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } - - case MemoComponent: { - { - if (workInProgress.type !== workInProgress.elementType) { - var outerPropTypes = Component.propTypes; - - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - resolvedProps, // Resolved for outer only - "prop", - getComponentNameFromType(Component) - ); - } - } - } - - child = updateMemoComponent( - null, - workInProgress, - Component, - resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too - updateLanes, - renderLanes - ); - return child; - } - } - - var hint = ""; - - { - if ( - Component !== null && - typeof Component === "object" && - Component.$$typeof === REACT_LAZY_TYPE - ) { - hint = " Did you wrap a component in React.lazy() more than once?"; - } - } // This message intentionally doesn't mention ForwardRef or MemoComponent - // because the fact that it's a separate type of work is an - // implementation detail. + if (current !== null && !didReceiveUpdate) { + bailoutHooks(current, workInProgress, renderLanes); + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } // React DevTools reads this flag. - { - throw Error( - "Element type is invalid. Received a promise that resolves to: " + - Component + - ". Lazy element type must resolve to a class or function." + - hint - ); - } + workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function mountIncompleteClassComponent( - _current, +function updateClassComponent( + current, workInProgress, Component, nextProps, renderLanes ) { - if (_current !== null) { - // An incomplete component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + { + // This is used by DevTools to force a boundary to error. + switch (shouldError(workInProgress)) { + case false: { + var _instance = workInProgress.stateNode; + var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack. + // Is there a better way to do this? + + var tempInstance = new ctor( + workInProgress.memoizedProps, + _instance.context + ); + var state = tempInstance.state; - workInProgress.flags |= Placement; - } // Promote the fiber to a class and try rendering again. + _instance.updater.enqueueSetState(_instance, state, null); - workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` - // Push context providers early to prevent context stack mismatches. + break; + } + + case true: { + workInProgress.flags |= DidCapture; + workInProgress.flags |= ShouldCapture; + var error$1 = new Error("Simulated error coming from DevTools"); + var lane = pickArbitraryLane(renderLanes); + workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state + + var update = createClassErrorUpdate( + workInProgress, + createCapturedValue(error$1, workInProgress), + lane + ); + enqueueCapturedUpdate(workInProgress, update); + break; + } + } + + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; + + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) + ); + } + } + } // Push context providers early to prevent context stack mismatches. // During mounting we don't know the child context yet as the instance doesn't exist. // We will invalidate the child context in finishClassComponent() right after rendering. @@ -13318,3440 +13566,3408 @@ function mountIncompleteClassComponent( } prepareToReadContext(workInProgress, renderLanes); - constructClassInstance(workInProgress, Component, nextProps); - mountClassInstance(workInProgress, Component, nextProps, renderLanes); - return finishClassComponent( - null, + var instance = workInProgress.stateNode; + var shouldUpdate; + + if (instance === null) { + if (current !== null) { + // A class component without an instance only mounts if it suspended + // inside a non-concurrent tree, in an inconsistent state. We want to + // treat it like a new mount, even though an empty version of it already + // committed. Disconnect the alternate pointers. + current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + + workInProgress.flags |= Placement; + } // In the initial pass we might need to construct the instance. + + constructClassInstance(workInProgress, Component, nextProps); + mountClassInstance(workInProgress, Component, nextProps, renderLanes); + shouldUpdate = true; + } else if (current === null) { + // In a resume, we'll already have an instance we can reuse. + shouldUpdate = resumeMountClassInstance( + workInProgress, + Component, + nextProps, + renderLanes + ); + } else { + shouldUpdate = updateClassInstance( + current, + workInProgress, + Component, + nextProps, + renderLanes + ); + } + + var nextUnitOfWork = finishClassComponent( + current, workInProgress, Component, - true, + shouldUpdate, hasContext, renderLanes ); + + { + var inst = workInProgress.stateNode; + + if (shouldUpdate && inst.props !== nextProps) { + if (!didWarnAboutReassigningProps) { + error( + "It looks like %s is reassigning its own `this.props` while rendering. " + + "This is not supported and can lead to confusing bugs.", + getComponentNameFromFiber(workInProgress) || "a component" + ); + } + + didWarnAboutReassigningProps = true; + } + } + + return nextUnitOfWork; } -function mountIndeterminateComponent( - _current, +function finishClassComponent( + current, workInProgress, Component, + shouldUpdate, + hasContext, renderLanes ) { - if (_current !== null) { - // An indeterminate component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + // Refs should update even if shouldComponentUpdate returns false + markRef(current, workInProgress); + var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; - workInProgress.flags |= Placement; + if (!shouldUpdate && !didCaptureError) { + // Context providers should defer to sCU for rendering + if (hasContext) { + invalidateContextProvider(workInProgress, Component, false); + } + + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } - var props = workInProgress.pendingProps; - var context; + var instance = workInProgress.stateNode; // Rerender - { - var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); - context = getMaskedContext(workInProgress, unmaskedContext); - } + ReactCurrentOwner$1.current = workInProgress; + var nextChildren; - prepareToReadContext(workInProgress, renderLanes); - var value; + if ( + didCaptureError && + typeof Component.getDerivedStateFromError !== "function" + ) { + // If we captured an error, but getDerivedStateFromError 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; - { - if ( - Component.prototype && - typeof Component.prototype.render === "function" - ) { - var componentName = getComponentNameFromType(Component) || "Unknown"; + { + stopProfilerTimerIfRunning(); + } + } else { + { + setIsRendering(true); + nextChildren = instance.render(); - if (!didWarnAboutBadClass[componentName]) { - error( - "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + - "This is likely to cause errors. Change %s to extend React.Component instead.", - componentName, - componentName - ); + if (workInProgress.mode & StrictLegacyMode) { + disableLogs(); - didWarnAboutBadClass[componentName] = true; + try { + instance.render(); + } finally { + reenableLogs(); + } } - } - if (workInProgress.mode & StrictLegacyMode) { - ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); + setIsRendering(false); } - - setIsRendering(true); - ReactCurrentOwner$1.current = workInProgress; - value = renderWithHooks( - null, - workInProgress, - Component, - props, - context, - renderLanes - ); - setIsRendering(false); - } // React DevTools reads this flag. + } // React DevTools reads this flag. workInProgress.flags |= PerformedWork; - { - // Support for module components is deprecated and is removed behind a flag. - // Whether or not it would crash later, we want to show a good message in DEV first. - if ( - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined - ) { - var _componentName = getComponentNameFromType(Component) || "Unknown"; + if (current !== null && didCaptureError) { + // If we're recovering from an error, reconcile without reusing any of + // the existing children. Conceptually, the normal children and the children + // that are shown on error are two different sets, so we shouldn't reuse + // normal children even if their identities match. + forceUnmountCurrentAndReconcile( + current, + workInProgress, + nextChildren, + renderLanes + ); + } else { + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } // Memoize state using the values we just used to render. + // TODO: Restructure so we never read values from the instance. - if (!didWarnAboutModulePatternComponent[_componentName]) { - error( - "The <%s /> component appears to be a function component that returns a class instance. " + - "Change %s to a class that extends React.Component instead. " + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - "cannot be called with `new` by React.", - _componentName, - _componentName, - _componentName - ); + workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. - didWarnAboutModulePatternComponent[_componentName] = true; - } - } + if (hasContext) { + invalidateContextProvider(workInProgress, Component, true); } - if ( - // Run these checks in production only if the flag is off. - // Eventually we'll delete this branch altogether. - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined - ) { - { - var _componentName2 = getComponentNameFromType(Component) || "Unknown"; + return workInProgress.child; +} - if (!didWarnAboutModulePatternComponent[_componentName2]) { - error( - "The <%s /> component appears to be a function component that returns a class instance. " + - "Change %s to a class that extends React.Component instead. " + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - "cannot be called with `new` by React.", - _componentName2, - _componentName2, - _componentName2 - ); +function pushHostRootContext(workInProgress) { + var root = workInProgress.stateNode; - didWarnAboutModulePatternComponent[_componentName2] = true; - } - } // Proceed under the assumption that this is a class instance + if (root.pendingContext) { + pushTopLevelContextObject( + workInProgress, + root.pendingContext, + root.pendingContext !== root.context + ); + } else if (root.context) { + // Should always be set + pushTopLevelContextObject(workInProgress, root.context, false); + } - workInProgress.tag = ClassComponent; // Throw out any hooks that were used. + pushHostContainer(workInProgress, root.containerInfo); +} - workInProgress.memoizedState = null; - workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. +function updateHostRoot(current, workInProgress, renderLanes) { + pushHostRootContext(workInProgress); + var updateQueue = workInProgress.updateQueue; - var hasContext = false; + if (!(current !== null && updateQueue !== null)) { + throw Error( + "If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue." + ); + } - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); - } else { - hasContext = false; - } + var nextProps = workInProgress.pendingProps; + var prevState = workInProgress.memoizedState; + var prevChildren = prevState.element; + cloneUpdateQueue(current, workInProgress); + processUpdateQueue(workInProgress, nextProps, null, renderLanes); + var nextState = workInProgress.memoizedState; + var root = workInProgress.stateNode; + // being called "element". - workInProgress.memoizedState = - value.state !== null && value.state !== undefined ? value.state : null; - initializeUpdateQueue(workInProgress); - adoptClassInstance(workInProgress, value); - mountClassInstance(workInProgress, Component, props, renderLanes); - return finishClassComponent( - null, + var nextChildren = nextState.element; + + if (nextChildren === prevChildren) { + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } + + if (root.hydrate && enterHydrationState()) { + var child = mountChildFibers( workInProgress, - Component, - true, - hasContext, + null, + nextChildren, renderLanes ); - } else { - // Proceed under the assumption that this is a function component - workInProgress.tag = FunctionComponent; - - { - if (workInProgress.mode & StrictLegacyMode) { - disableLogs(); + workInProgress.child = child; + var node = child; - try { - value = renderWithHooks( - null, - workInProgress, - Component, - props, - context, - renderLanes - ); - } finally { - reenableLogs(); - } - } + while (node) { + // Mark each child as hydrating. This is a fast path to know whether this + // tree is part of a hydrating tree. This is used to determine if a child + // node has fully mounted yet, and for scheduling event replaying. + // Conceptually this is similar to Placement in that a new subtree is + // inserted into the React tree here. It just happens to not need DOM + // mutations because it already exists. + node.flags = (node.flags & ~Placement) | Hydrating; + node = node.sibling; } + } else { + // Otherwise reset hydration state in case we aborted and resumed another + // root. + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } - reconcileChildren(null, workInProgress, value, renderLanes); + return workInProgress.child; +} - { - validateFunctionComponentInDev(workInProgress, Component); - } +function updateHostComponent(current, workInProgress, renderLanes) { + pushHostContext(workInProgress); - return workInProgress.child; + var type = workInProgress.type; + var nextProps = workInProgress.pendingProps; + var prevProps = current !== null ? current.memoizedProps : null; + var nextChildren = nextProps.children; + + if (prevProps !== null && shouldSetTextContent()) { + // If we're switching from a direct text child to a normal child, or to + // empty, we need to schedule the text content to be reset. + workInProgress.flags |= ContentReset; } + + markRef(current, workInProgress); + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function validateFunctionComponentInDev(workInProgress, Component) { - { - if (Component) { - if (Component.childContextTypes) { - error( - "%s(...): childContextTypes cannot be defined on a function component.", - Component.displayName || Component.name || "Component" - ); - } - } +function updateHostText(current, workInProgress) { + // immediately after. - if (workInProgress.ref !== null) { - var info = ""; - var ownerName = getCurrentFiberOwnerNameInDevOrNull(); + return null; +} - if (ownerName) { - info += "\n\nCheck the render method of `" + ownerName + "`."; - } +function mountLazyComponent( + _current, + workInProgress, + elementType, + updateLanes, + renderLanes +) { + if (_current !== null) { + // A lazy component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - var warningKey = ownerName || workInProgress._debugID || ""; - var debugSource = workInProgress._debugSource; + workInProgress.flags |= Placement; + } - if (debugSource) { - warningKey = debugSource.fileName + ":" + debugSource.lineNumber; - } + var props = workInProgress.pendingProps; + var lazyComponent = elementType; + var payload = lazyComponent._payload; + var init = lazyComponent._init; + var Component = init(payload); // Store the unwrapped component in the type. - if (!didWarnAboutFunctionRefs[warningKey]) { - didWarnAboutFunctionRefs[warningKey] = true; + workInProgress.type = Component; + var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); + var resolvedProps = resolveDefaultProps(Component, props); + var child; - error( - "Function components cannot be given refs. " + - "Attempts to access this ref will fail. " + - "Did you mean to use React.forwardRef()?%s", - info + switch (resolvedTag) { + case FunctionComponent: { + { + validateFunctionComponentInDev(workInProgress, Component); + workInProgress.type = Component = resolveFunctionForHotReloading( + Component ); } - } - if (typeof Component.getDerivedStateFromProps === "function") { - var _componentName3 = getComponentNameFromType(Component) || "Unknown"; + child = updateFunctionComponent( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; + } - if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { - error( - "%s: Function components do not support getDerivedStateFromProps.", - _componentName3 + case ClassComponent: { + { + workInProgress.type = Component = resolveClassForHotReloading( + Component ); - - didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; } - } - if ( - typeof Component.contextType === "object" && - Component.contextType !== null - ) { - var _componentName4 = getComponentNameFromType(Component) || "Unknown"; + child = updateClassComponent( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; + } - if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { - error( - "%s: Function components do not support contextType.", - _componentName4 + case ForwardRef: { + { + workInProgress.type = Component = resolveForwardRefForHotReloading( + Component ); + } - didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; + child = updateForwardRef( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; + } + + case MemoComponent: { + { + if (workInProgress.type !== workInProgress.elementType) { + var outerPropTypes = Component.propTypes; + + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + resolvedProps, // Resolved for outer only + "prop", + getComponentNameFromType(Component) + ); + } + } } + + child = updateMemoComponent( + null, + workInProgress, + Component, + resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too + updateLanes, + renderLanes + ); + return child; } } -} - -var SUSPENDED_MARKER = { - dehydrated: null, - retryLane: NoLane -}; -function mountSuspenseOffscreenState(renderLanes) { - return { - baseLanes: renderLanes, - cachePool: getSuspendedCachePool() - }; -} + var hint = ""; -function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { - var cachePool = null; + { + if ( + Component !== null && + typeof Component === "object" && + Component.$$typeof === REACT_LAZY_TYPE + ) { + hint = " Did you wrap a component in React.lazy() more than once?"; + } + } // This message intentionally doesn't mention ForwardRef or MemoComponent + // because the fact that it's a separate type of work is an + // implementation detail. - return { - baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), - cachePool: cachePool - }; -} // TODO: Probably should inline this back + { + throw Error( + "Element type is invalid. Received a promise that resolves to: " + + Component + + ". Lazy element type must resolve to a class or function." + + hint + ); + } +} -function shouldRemainOnFallback( - suspenseContext, - current, +function mountIncompleteClassComponent( + _current, workInProgress, + Component, + nextProps, renderLanes ) { - // If we're already showing a fallback, there are cases where we need to - // remain on that fallback regardless of whether the content has resolved. - // For example, SuspenseList coordinates when nested content appears. - if (current !== null) { - var suspenseState = current.memoizedState; + if (_current !== null) { + // An incomplete component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - if (suspenseState === null) { - // Currently showing content. Don't hide it, even if ForceSuspenseFallack - // is true. More precise name might be "ForceRemainSuspenseFallback". - // Note: This is a factoring smell. Can't remain on a fallback if there's - // no fallback to remain on. - return false; - } - } // Not currently showing content. Consult the Suspense context. + workInProgress.flags |= Placement; + } // Promote the fiber to a class and try rendering again. - return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); -} + workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` + // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. -function getRemainingWorkInPrimaryTree(current, renderLanes) { - // TODO: Should not remove render lanes that were pinged during this render - return removeLanes(current.childLanes, renderLanes); + var hasContext; + + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; + } + + prepareToReadContext(workInProgress, renderLanes); + constructClassInstance(workInProgress, Component, nextProps); + mountClassInstance(workInProgress, Component, nextProps, renderLanes); + return finishClassComponent( + null, + workInProgress, + Component, + true, + hasContext, + renderLanes + ); } -function updateSuspenseComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. +function mountIndeterminateComponent( + _current, + workInProgress, + Component, + renderLanes +) { + if (_current !== null) { + // An indeterminate component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - { - if (shouldSuspend(workInProgress)) { - workInProgress.flags |= DidCapture; - } + workInProgress.flags |= Placement; } - var suspenseContext = suspenseStackCursor.current; - var showFallback = false; - var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; + var props = workInProgress.pendingProps; + var context; - if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { - // Something in this boundary's subtree already suspended. Switch to - // rendering the fallback children. - showFallback = true; - workInProgress.flags &= ~DidCapture; - } else { - // Attempting the main content - if (current === null || current.memoizedState !== null) { - // This is a new mount or this boundary is already showing a fallback state. - // Mark this subtree context as having at least one invisible parent that could - // handle the fallback state. - // Boundaries without fallbacks or should be avoided are not considered since - // they cannot handle preferred fallback states. - if ( - nextProps.fallback !== undefined && - nextProps.unstable_avoidThisFallback !== true - ) { - suspenseContext = addSubtreeSuspenseContext( - suspenseContext, - InvisibleParentSuspenseContext - ); - } - } + { + var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); + context = getMaskedContext(workInProgress, unmaskedContext); } - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense - // boundary's children. This involves some custom reconcilation logic. Two - // main reasons this is so complicated. - // - // First, Legacy Mode has different semantics for backwards compatibility. The - // primary tree will commit in an inconsistent state, so when we do the - // second pass to render the fallback, we do some exceedingly, uh, clever - // hacks to make that not totally break. Like transferring effects and - // deletions from hidden tree. In Concurrent Mode, it's much simpler, - // because we bailout on the primary tree completely and leave it in its old - // state, no effects. Same as what we do for Offscreen (except that - // Offscreen doesn't have the first render pass). - // - // Second is hydration. During hydration, the Suspense fiber has a slightly - // different layout, where the child points to a dehydrated fragment, which - // contains the DOM rendered by the server. - // - // Third, even if you set all that aside, Suspense is like error boundaries in - // that we first we try to render one tree, and if that fails, we render again - // and switch to a different tree. Like a try/catch block. So we have to track - // which branch we're currently rendering. Ideally we would model this using - // a stack. - - if (current === null) { - // Initial mount - // If we're currently hydrating, try to hydrate this boundary. - // But only if this has a fallback. - if (nextProps.fallback !== undefined); + prepareToReadContext(workInProgress, renderLanes); + var value; - var nextPrimaryChildren = nextProps.children; - var nextFallbackChildren = nextProps.fallback; + { + if ( + Component.prototype && + typeof Component.prototype.render === "function" + ) { + var componentName = getComponentNameFromType(Component) || "Unknown"; - if (showFallback) { - var fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ); - var primaryChildFragment = workInProgress.child; - primaryChildFragment.memoizedState = mountSuspenseOffscreenState( - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; - return fallbackFragment; - } else if (typeof nextProps.unstable_expectedLoadTime === "number") { - // This is a CPU-bound tree. Skip this tree and show a placeholder to - // unblock the surrounding content. Then immediately retry after the - // initial commit. - var _fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ); + if (!didWarnAboutBadClass[componentName]) { + error( + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + + "This is likely to cause errors. Change %s to extend React.Component instead.", + componentName, + componentName + ); - var _primaryChildFragment = workInProgress.child; - _primaryChildFragment.memoizedState = mountSuspenseOffscreenState( - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; // Since nothing actually suspended, there will nothing to ping this to - // get it started back up to attempt the next item. While in terms of - // priority this work has the same priority as this current render, it's - // not part of the same transition once the transition has committed. If - // it's sync, we still want to yield so that it can be painted. - // Conceptually, this is really the same as pinging. We can use any - // RetryLane even if it's the one currently rendering since we're leaving - // it behind on this node. + didWarnAboutBadClass[componentName] = true; + } + } - workInProgress.lanes = SomeRetryLane; - return _fallbackFragment; - } else { - return mountSuspensePrimaryChildren( - workInProgress, - nextPrimaryChildren, - renderLanes - ); + if (workInProgress.mode & StrictLegacyMode) { + ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); } - } else { - // This is an update. - // If the current fiber has a SuspenseState, that means it's already showing - // a fallback. - var prevState = current.memoizedState; - if (prevState !== null) { - if (showFallback) { - var _nextFallbackChildren2 = nextProps.fallback; - var _nextPrimaryChildren2 = nextProps.children; + setIsRendering(true); + ReactCurrentOwner$1.current = workInProgress; + value = renderWithHooks( + null, + workInProgress, + Component, + props, + context, + renderLanes + ); + setIsRendering(false); + } // React DevTools reads this flag. - var _fallbackChildFragment = updateSuspenseFallbackChildren( - current, - workInProgress, - _nextPrimaryChildren2, - _nextFallbackChildren2, - renderLanes - ); + workInProgress.flags |= PerformedWork; - var _primaryChildFragment3 = workInProgress.child; - var prevOffscreenState = current.child.memoizedState; - _primaryChildFragment3.memoizedState = - prevOffscreenState === null - ? mountSuspenseOffscreenState(renderLanes) - : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); - _primaryChildFragment3.childLanes = getRemainingWorkInPrimaryTree( - current, - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; - return _fallbackChildFragment; - } else { - var _nextPrimaryChildren3 = nextProps.children; + { + // Support for module components is deprecated and is removed behind a flag. + // Whether or not it would crash later, we want to show a good message in DEV first. + if ( + typeof value === "object" && + value !== null && + typeof value.render === "function" && + value.$$typeof === undefined + ) { + var _componentName = getComponentNameFromType(Component) || "Unknown"; - var _primaryChildFragment4 = updateSuspensePrimaryChildren( - current, - workInProgress, - _nextPrimaryChildren3, - renderLanes + if (!didWarnAboutModulePatternComponent[_componentName]) { + error( + "The <%s /> component appears to be a function component that returns a class instance. " + + "Change %s to a class that extends React.Component instead. " + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + + "cannot be called with `new` by React.", + _componentName, + _componentName, + _componentName ); - workInProgress.memoizedState = null; - return _primaryChildFragment4; + didWarnAboutModulePatternComponent[_componentName] = true; } - } else { - // The current tree is not already showing a fallback. - if (showFallback) { - // Timed out. - var _nextFallbackChildren3 = nextProps.fallback; - var _nextPrimaryChildren4 = nextProps.children; - - var _fallbackChildFragment2 = updateSuspenseFallbackChildren( - current, - workInProgress, - _nextPrimaryChildren4, - _nextFallbackChildren3, - renderLanes - ); - - var _primaryChildFragment5 = workInProgress.child; - var _prevOffscreenState = current.child.memoizedState; - _primaryChildFragment5.memoizedState = - _prevOffscreenState === null - ? mountSuspenseOffscreenState(renderLanes) - : updateSuspenseOffscreenState(_prevOffscreenState, renderLanes); - _primaryChildFragment5.childLanes = getRemainingWorkInPrimaryTree( - current, - renderLanes - ); // Skip the primary children, and continue working on the - // fallback children. + } + } - workInProgress.memoizedState = SUSPENDED_MARKER; - return _fallbackChildFragment2; - } else { - // Still haven't timed out. Continue rendering the children, like we - // normally do. - var _nextPrimaryChildren5 = nextProps.children; + if ( + // Run these checks in production only if the flag is off. + // Eventually we'll delete this branch altogether. + typeof value === "object" && + value !== null && + typeof value.render === "function" && + value.$$typeof === undefined + ) { + { + var _componentName2 = getComponentNameFromType(Component) || "Unknown"; - var _primaryChildFragment6 = updateSuspensePrimaryChildren( - current, - workInProgress, - _nextPrimaryChildren5, - renderLanes + if (!didWarnAboutModulePatternComponent[_componentName2]) { + error( + "The <%s /> component appears to be a function component that returns a class instance. " + + "Change %s to a class that extends React.Component instead. " + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + + "cannot be called with `new` by React.", + _componentName2, + _componentName2, + _componentName2 ); - workInProgress.memoizedState = null; - return _primaryChildFragment6; + didWarnAboutModulePatternComponent[_componentName2] = true; } - } - } -} + } // Proceed under the assumption that this is a class instance -function mountSuspensePrimaryChildren( - workInProgress, - primaryChildren, - renderLanes -) { - var mode = workInProgress.mode; - var primaryChildProps = { - mode: "visible", - children: primaryChildren - }; - var primaryChildFragment = createFiberFromOffscreen( - primaryChildProps, - mode, - renderLanes, - null - ); - primaryChildFragment.return = workInProgress; - workInProgress.child = primaryChildFragment; - return primaryChildFragment; -} + workInProgress.tag = ClassComponent; // Throw out any hooks that were used. -function mountSuspenseFallbackChildren( - workInProgress, - primaryChildren, - fallbackChildren, - renderLanes -) { - var mode = workInProgress.mode; - var progressedPrimaryFragment = workInProgress.child; - var primaryChildProps = { - mode: "hidden", - children: primaryChildren - }; - var primaryChildFragment; - var fallbackChildFragment; + workInProgress.memoizedState = null; + workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. - if ( - (mode & ConcurrentMode) === NoMode && - progressedPrimaryFragment !== null - ) { - // In legacy mode, we commit the primary tree as if it successfully - // completed, even though it's in an inconsistent state. - primaryChildFragment = progressedPrimaryFragment; - primaryChildFragment.childLanes = NoLanes; - primaryChildFragment.pendingProps = primaryChildProps; + var hasContext = false; - if (workInProgress.mode & ProfileMode) { - // Reset the durations from the first pass so they aren't included in the - // final amounts. This seems counterintuitive, since we're intentionally - // not measuring part of the render phase, but this makes it match what we - // do in Concurrent Mode. - primaryChildFragment.actualDuration = 0; - primaryChildFragment.actualStartTime = -1; - primaryChildFragment.selfBaseDuration = 0; - primaryChildFragment.treeBaseDuration = 0; + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; } - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null + workInProgress.memoizedState = + value.state !== null && value.state !== undefined ? value.state : null; + initializeUpdateQueue(workInProgress); + adoptClassInstance(workInProgress, value); + mountClassInstance(workInProgress, Component, props, renderLanes); + return finishClassComponent( + null, + workInProgress, + Component, + true, + hasContext, + renderLanes ); } else { - primaryChildFragment = createFiberFromOffscreen( - primaryChildProps, - mode, - NoLanes, - null - ); - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null - ); - } + // Proceed under the assumption that this is a function component + workInProgress.tag = FunctionComponent; - primaryChildFragment.return = workInProgress; - fallbackChildFragment.return = workInProgress; - primaryChildFragment.sibling = fallbackChildFragment; - workInProgress.child = primaryChildFragment; - return fallbackChildFragment; -} + { + if (workInProgress.mode & StrictLegacyMode) { + disableLogs(); -function createWorkInProgressOffscreenFiber(current, offscreenProps) { - // The props argument to `createWorkInProgress` is `any` typed, so we use this - // wrapper function to constrain it. - return createWorkInProgress(current, offscreenProps); -} + try { + value = renderWithHooks( + null, + workInProgress, + Component, + props, + context, + renderLanes + ); + } finally { + reenableLogs(); + } + } + } + + reconcileChildren(null, workInProgress, value, renderLanes); -function updateSuspensePrimaryChildren( - current, - workInProgress, - primaryChildren, - renderLanes -) { - var currentPrimaryChildFragment = current.child; - var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; - var primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, { - mode: "visible", - children: primaryChildren + validateFunctionComponentInDev(workInProgress, Component); } - ); - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - primaryChildFragment.lanes = renderLanes; + return workInProgress.child; } +} - primaryChildFragment.return = workInProgress; - primaryChildFragment.sibling = null; +function validateFunctionComponentInDev(workInProgress, Component) { + { + if (Component) { + if (Component.childContextTypes) { + error( + "%s(...): childContextTypes cannot be defined on a function component.", + Component.displayName || Component.name || "Component" + ); + } + } - if (currentFallbackChildFragment !== null) { - // Delete the fallback child fragment - var deletions = workInProgress.deletions; + if (workInProgress.ref !== null) { + var info = ""; + var ownerName = getCurrentFiberOwnerNameInDevOrNull(); - if (deletions === null) { - workInProgress.deletions = [currentFallbackChildFragment]; - workInProgress.flags |= ChildDeletion; - } else { - deletions.push(currentFallbackChildFragment); - } - } + if (ownerName) { + info += "\n\nCheck the render method of `" + ownerName + "`."; + } - workInProgress.child = primaryChildFragment; - return primaryChildFragment; -} + var warningKey = ownerName || ""; + var debugSource = workInProgress._debugSource; -function updateSuspenseFallbackChildren( - current, - workInProgress, - primaryChildren, - fallbackChildren, - renderLanes -) { - var mode = workInProgress.mode; - var currentPrimaryChildFragment = current.child; - var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; - var primaryChildProps = { - mode: "hidden", - children: primaryChildren - }; - var primaryChildFragment; + if (debugSource) { + warningKey = debugSource.fileName + ":" + debugSource.lineNumber; + } - if ( - // In legacy mode, we commit the primary tree as if it successfully - // completed, even though it's in an inconsistent state. - (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was - // already cloned. In legacy mode, the only case where this isn't true is - // when DevTools forces us to display a fallback; we skip the first render - // pass entirely and go straight to rendering the fallback. (In Concurrent - // Mode, SuspenseList can also trigger this scenario, but this is a legacy- - // only codepath.) - workInProgress.child !== currentPrimaryChildFragment - ) { - var progressedPrimaryFragment = workInProgress.child; - primaryChildFragment = progressedPrimaryFragment; - primaryChildFragment.childLanes = NoLanes; - primaryChildFragment.pendingProps = primaryChildProps; + if (!didWarnAboutFunctionRefs[warningKey]) { + didWarnAboutFunctionRefs[warningKey] = true; - if (workInProgress.mode & ProfileMode) { - // Reset the durations from the first pass so they aren't included in the - // final amounts. This seems counterintuitive, since we're intentionally - // not measuring part of the render phase, but this makes it match what we - // do in Concurrent Mode. - primaryChildFragment.actualDuration = 0; - primaryChildFragment.actualStartTime = -1; - primaryChildFragment.selfBaseDuration = - currentPrimaryChildFragment.selfBaseDuration; - primaryChildFragment.treeBaseDuration = - currentPrimaryChildFragment.treeBaseDuration; - } // The fallback fiber was added as a deletion during the first pass. - // However, since we're going to remain on the fallback, we no longer want - // to delete it. + error( + "Function components cannot be given refs. " + + "Attempts to access this ref will fail. " + + "Did you mean to use React.forwardRef()?%s", + info + ); + } + } - workInProgress.deletions = null; - } else { - primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, - primaryChildProps - ); // Since we're reusing a current tree, we need to reuse the flags, too. - // (We don't do this in legacy mode, because in legacy mode we don't re-use - // the current tree; see previous branch.) + if (typeof Component.getDerivedStateFromProps === "function") { + var _componentName3 = getComponentNameFromType(Component) || "Unknown"; + + if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { + error( + "%s: Function components do not support getDerivedStateFromProps.", + _componentName3 + ); - primaryChildFragment.subtreeFlags = - currentPrimaryChildFragment.subtreeFlags & StaticMask; - } + didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; + } + } - var fallbackChildFragment; + if ( + typeof Component.contextType === "object" && + Component.contextType !== null + ) { + var _componentName4 = getComponentNameFromType(Component) || "Unknown"; - if (currentFallbackChildFragment !== null) { - fallbackChildFragment = createWorkInProgress( - currentFallbackChildFragment, - fallbackChildren - ); - } else { - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null - ); // Needs a placement effect because the parent (the Suspense boundary) already - // mounted but this is a new fiber. + if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { + error( + "%s: Function components do not support contextType.", + _componentName4 + ); - fallbackChildFragment.flags |= Placement; + didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; + } + } } - - fallbackChildFragment.return = workInProgress; - primaryChildFragment.return = workInProgress; - primaryChildFragment.sibling = fallbackChildFragment; - workInProgress.child = primaryChildFragment; - return fallbackChildFragment; } -function scheduleWorkOnFiber(fiber, renderLanes) { - fiber.lanes = mergeLanes(fiber.lanes, renderLanes); - var alternate = fiber.alternate; - - if (alternate !== null) { - alternate.lanes = mergeLanes(alternate.lanes, renderLanes); - } +var SUSPENDED_MARKER = { + dehydrated: null, + retryLane: NoLane +}; - scheduleWorkOnParentPath(fiber.return, renderLanes); +function mountSuspenseOffscreenState(renderLanes) { + return { + baseLanes: renderLanes, + cachePool: getSuspendedCachePool() + }; } -function propagateSuspenseContextChange( +function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { + var cachePool = null; + + return { + baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), + cachePool: cachePool + }; +} // TODO: Probably should inline this back + +function shouldRemainOnFallback( + suspenseContext, + current, workInProgress, - firstChild, renderLanes ) { - // Mark any Suspense boundaries with fallbacks as having work to do. - // If they were previously forced into fallbacks, they may now be able - // to unblock. - var node = firstChild; - - while (node !== null) { - if (node.tag === SuspenseComponent) { - var state = node.memoizedState; - - if (state !== null) { - scheduleWorkOnFiber(node, renderLanes); - } - } else if (node.tag === SuspenseListComponent) { - // If the tail is hidden there might not be an Suspense boundaries - // to schedule work on. In this case we have to schedule it on the - // list itself. - // We don't have to traverse to the children of the list since - // the list will propagate the change when it rerenders. - scheduleWorkOnFiber(node, renderLanes); - } else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } - - if (node === workInProgress) { - return; - } - - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; - } + // If we're already showing a fallback, there are cases where we need to + // remain on that fallback regardless of whether the content has resolved. + // For example, SuspenseList coordinates when nested content appears. + if (current !== null) { + var suspenseState = current.memoizedState; - node = node.return; + if (suspenseState === null) { + // Currently showing content. Don't hide it, even if ForceSuspenseFallack + // is true. More precise name might be "ForceRemainSuspenseFallback". + // Note: This is a factoring smell. Can't remain on a fallback if there's + // no fallback to remain on. + return false; } + } // Not currently showing content. Consult the Suspense context. - node.sibling.return = node.return; - node = node.sibling; - } + return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); } -function findLastContentRow(firstChild) { - // This is going to find the last row among these children that is already - // showing content on the screen, as opposed to being in fallback state or - // new. If a row has multiple Suspense boundaries, any of them being in the - // fallback state, counts as the whole row being in a fallback state. - // Note that the "rows" will be workInProgress, but any nested children - // will still be current since we haven't rendered them yet. The mounted - // order may not be the same as the new order. We use the new order. - var row = firstChild; - var lastContentRow = null; +function getRemainingWorkInPrimaryTree(current, renderLanes) { + // TODO: Should not remove render lanes that were pinged during this render + return removeLanes(current.childLanes, renderLanes); +} - while (row !== null) { - var currentRow = row.alternate; // New rows can't be content rows. +function updateSuspenseComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. - if (currentRow !== null && findFirstSuspended(currentRow) === null) { - lastContentRow = row; + { + if (shouldSuspend(workInProgress)) { + workInProgress.flags |= DidCapture; } - - row = row.sibling; } - return lastContentRow; -} + var suspenseContext = suspenseStackCursor.current; + var showFallback = false; + var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; -function validateRevealOrder(revealOrder) { - { - if ( - revealOrder !== undefined && - revealOrder !== "forwards" && - revealOrder !== "backwards" && - revealOrder !== "together" && - !didWarnAboutRevealOrder[revealOrder] - ) { - didWarnAboutRevealOrder[revealOrder] = true; + if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { + // Something in this boundary's subtree already suspended. Switch to + // rendering the fallback children. + showFallback = true; + workInProgress.flags &= ~DidCapture; + } else { + // Attempting the main content + if (current === null || current.memoizedState !== null) { + // This is a new mount or this boundary is already showing a fallback state. + // Mark this subtree context as having at least one invisible parent that could + // handle the fallback state. + // Boundaries without fallbacks or should be avoided are not considered since + // they cannot handle preferred fallback states. + if ( + nextProps.fallback !== undefined && + nextProps.unstable_avoidThisFallback !== true + ) { + suspenseContext = addSubtreeSuspenseContext( + suspenseContext, + InvisibleParentSuspenseContext + ); + } + } + } - if (typeof revealOrder === "string") { - switch (revealOrder.toLowerCase()) { - case "together": - case "forwards": - case "backwards": { - error( - '"%s" is not a valid value for revealOrder on . ' + - 'Use lowercase "%s" instead.', - revealOrder, - revealOrder.toLowerCase() - ); + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense + // boundary's children. This involves some custom reconcilation logic. Two + // main reasons this is so complicated. + // + // First, Legacy Mode has different semantics for backwards compatibility. The + // primary tree will commit in an inconsistent state, so when we do the + // second pass to render the fallback, we do some exceedingly, uh, clever + // hacks to make that not totally break. Like transferring effects and + // deletions from hidden tree. In Concurrent Mode, it's much simpler, + // because we bailout on the primary tree completely and leave it in its old + // state, no effects. Same as what we do for Offscreen (except that + // Offscreen doesn't have the first render pass). + // + // Second is hydration. During hydration, the Suspense fiber has a slightly + // different layout, where the child points to a dehydrated fragment, which + // contains the DOM rendered by the server. + // + // Third, even if you set all that aside, Suspense is like error boundaries in + // that we first we try to render one tree, and if that fails, we render again + // and switch to a different tree. Like a try/catch block. So we have to track + // which branch we're currently rendering. Ideally we would model this using + // a stack. - break; - } + if (current === null) { + // Initial mount + // If we're currently hydrating, try to hydrate this boundary. + // But only if this has a fallback. + if (nextProps.fallback !== undefined); - case "forward": - case "backward": { - error( - '"%s" is not a valid value for revealOrder on . ' + - 'React uses the -s suffix in the spelling. Use "%ss" instead.', - revealOrder, - revealOrder.toLowerCase() - ); + var nextPrimaryChildren = nextProps.children; + var nextFallbackChildren = nextProps.fallback; - break; - } + if (showFallback) { + var fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); + var primaryChildFragment = workInProgress.child; + primaryChildFragment.memoizedState = mountSuspenseOffscreenState( + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return fallbackFragment; + } else if (typeof nextProps.unstable_expectedLoadTime === "number") { + // This is a CPU-bound tree. Skip this tree and show a placeholder to + // unblock the surrounding content. Then immediately retry after the + // initial commit. + var _fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); - default: - error( - '"%s" is not a supported revealOrder on . ' + - 'Did you mean "together", "forwards" or "backwards"?', - revealOrder - ); + var _primaryChildFragment = workInProgress.child; + _primaryChildFragment.memoizedState = mountSuspenseOffscreenState( + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; // Since nothing actually suspended, there will nothing to ping this to + // get it started back up to attempt the next item. While in terms of + // priority this work has the same priority as this current render, it's + // not part of the same transition once the transition has committed. If + // it's sync, we still want to yield so that it can be painted. + // Conceptually, this is really the same as pinging. We can use any + // RetryLane even if it's the one currently rendering since we're leaving + // it behind on this node. - break; - } - } else { - error( - "%s is not a supported value for revealOrder on . " + - 'Did you mean "together", "forwards" or "backwards"?', - revealOrder - ); - } + workInProgress.lanes = SomeRetryLane; + return _fallbackFragment; + } else { + return mountSuspensePrimaryChildren( + workInProgress, + nextPrimaryChildren, + renderLanes + ); } - } -} + } else { + // This is an update. + // If the current fiber has a SuspenseState, that means it's already showing + // a fallback. + var prevState = current.memoizedState; -function validateTailOptions(tailMode, revealOrder) { - { - if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { - if (tailMode !== "collapsed" && tailMode !== "hidden") { - didWarnAboutTailOptions[tailMode] = true; + if (prevState !== null) { + if (showFallback) { + var _nextFallbackChildren2 = nextProps.fallback; + var _nextPrimaryChildren2 = nextProps.children; - error( - '"%s" is not a supported value for tail on . ' + - 'Did you mean "collapsed" or "hidden"?', - tailMode + var _fallbackChildFragment = updateSuspenseFallbackChildren( + current, + workInProgress, + _nextPrimaryChildren2, + _nextFallbackChildren2, + renderLanes ); - } else if (revealOrder !== "forwards" && revealOrder !== "backwards") { - didWarnAboutTailOptions[tailMode] = true; - error( - ' is only valid if revealOrder is ' + - '"forwards" or "backwards". ' + - 'Did you mean to specify revealOrder="forwards"?', - tailMode + var _primaryChildFragment3 = workInProgress.child; + var prevOffscreenState = current.child.memoizedState; + _primaryChildFragment3.memoizedState = + prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); + _primaryChildFragment3.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return _fallbackChildFragment; + } else { + var _nextPrimaryChildren3 = nextProps.children; + + var _primaryChildFragment4 = updateSuspensePrimaryChildren( + current, + workInProgress, + _nextPrimaryChildren3, + renderLanes ); + + workInProgress.memoizedState = null; + return _primaryChildFragment4; } - } - } -} + } else { + // The current tree is not already showing a fallback. + if (showFallback) { + // Timed out. + var _nextFallbackChildren3 = nextProps.fallback; + var _nextPrimaryChildren4 = nextProps.children; -function validateSuspenseListNestedChild(childSlot, index) { - { - var isAnArray = isArray(childSlot); - var isIterable = - !isAnArray && typeof getIteratorFn(childSlot) === "function"; + var _fallbackChildFragment2 = updateSuspenseFallbackChildren( + current, + workInProgress, + _nextPrimaryChildren4, + _nextFallbackChildren3, + renderLanes + ); - if (isAnArray || isIterable) { - var type = isAnArray ? "array" : "iterable"; + var _primaryChildFragment5 = workInProgress.child; + var _prevOffscreenState = current.child.memoizedState; + _primaryChildFragment5.memoizedState = + _prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(_prevOffscreenState, renderLanes); + _primaryChildFragment5.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes + ); // Skip the primary children, and continue working on the + // fallback children. - error( - "A nested %s was passed to row #%s in . Wrap it in " + - "an additional SuspenseList to configure its revealOrder: " + - " ... " + - "{%s} ... " + - "", - type, - index, - type - ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return _fallbackChildFragment2; + } else { + // Still haven't timed out. Continue rendering the children, like we + // normally do. + var _nextPrimaryChildren5 = nextProps.children; - return false; + var _primaryChildFragment6 = updateSuspensePrimaryChildren( + current, + workInProgress, + _nextPrimaryChildren5, + renderLanes + ); + + workInProgress.memoizedState = null; + return _primaryChildFragment6; + } } } - - return true; } -function validateSuspenseListChildren(children, revealOrder) { - { - if ( - (revealOrder === "forwards" || revealOrder === "backwards") && - children !== undefined && - children !== null && - children !== false - ) { - if (isArray(children)) { - for (var i = 0; i < children.length; i++) { - if (!validateSuspenseListNestedChild(children[i], i)) { - return; - } - } - } else { - var iteratorFn = getIteratorFn(children); - - if (typeof iteratorFn === "function") { - var childrenIterator = iteratorFn.call(children); +function mountSuspensePrimaryChildren( + workInProgress, + primaryChildren, + renderLanes +) { + var mode = workInProgress.mode; + var primaryChildProps = { + mode: "visible", + children: primaryChildren + }; + var primaryChildFragment = createFiberFromOffscreen( + primaryChildProps, + mode, + renderLanes, + null + ); + primaryChildFragment.return = workInProgress; + workInProgress.child = primaryChildFragment; + return primaryChildFragment; +} - if (childrenIterator) { - var step = childrenIterator.next(); - var _i = 0; +function mountSuspenseFallbackChildren( + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + var mode = workInProgress.mode; + var progressedPrimaryFragment = workInProgress.child; + var primaryChildProps = { + mode: "hidden", + children: primaryChildren + }; + var primaryChildFragment; + var fallbackChildFragment; - for (; !step.done; step = childrenIterator.next()) { - if (!validateSuspenseListNestedChild(step.value, _i)) { - return; - } + if ( + (mode & ConcurrentMode) === NoMode && + progressedPrimaryFragment !== null + ) { + // In legacy mode, we commit the primary tree as if it successfully + // completed, even though it's in an inconsistent state. + primaryChildFragment = progressedPrimaryFragment; + primaryChildFragment.childLanes = NoLanes; + primaryChildFragment.pendingProps = primaryChildProps; - _i++; - } - } - } else { - error( - 'A single row was passed to a . ' + - "This is not useful since it needs multiple rows. " + - "Did you mean to pass multiple children or an array?", - revealOrder - ); - } - } + if (workInProgress.mode & ProfileMode) { + // Reset the durations from the first pass so they aren't included in the + // final amounts. This seems counterintuitive, since we're intentionally + // not measuring part of the render phase, but this makes it match what we + // do in Concurrent Mode. + primaryChildFragment.actualDuration = 0; + primaryChildFragment.actualStartTime = -1; + primaryChildFragment.selfBaseDuration = 0; + primaryChildFragment.treeBaseDuration = 0; } + + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); + } else { + primaryChildFragment = createFiberFromOffscreen( + primaryChildProps, + mode, + NoLanes, + null + ); + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); } + + primaryChildFragment.return = workInProgress; + fallbackChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; } -function initSuspenseListRenderState( +function createWorkInProgressOffscreenFiber(current, offscreenProps) { + // The props argument to `createWorkInProgress` is `any` typed, so we use this + // wrapper function to constrain it. + return createWorkInProgress(current, offscreenProps); +} + +function updateSuspensePrimaryChildren( + current, workInProgress, - isBackwards, - tail, - lastContentRow, - tailMode + primaryChildren, + renderLanes ) { - var renderState = workInProgress.memoizedState; + var currentPrimaryChildFragment = current.child; + var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; + var primaryChildFragment = createWorkInProgressOffscreenFiber( + currentPrimaryChildFragment, + { + mode: "visible", + children: primaryChildren + } + ); - if (renderState === null) { - workInProgress.memoizedState = { - isBackwards: isBackwards, - rendering: null, - renderingStartTime: 0, - last: lastContentRow, - tail: tail, - tailMode: tailMode - }; - } else { - // We can reuse the existing object from previous renders. - renderState.isBackwards = isBackwards; - renderState.rendering = null; - renderState.renderingStartTime = 0; - renderState.last = lastContentRow; - renderState.tail = tail; - renderState.tailMode = tailMode; + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + primaryChildFragment.lanes = renderLanes; } -} // This can end up rendering this component multiple passes. -// The first pass splits the children fibers into two sets. A head and tail. -// We first render the head. If anything is in fallback state, we do another -// pass through beginWork to rerender all children (including the tail) with -// the force suspend context. If the first render didn't have anything in -// in fallback state. Then we render each row in the tail one-by-one. -// That happens in the completeWork phase without going back to beginWork. -function updateSuspenseListComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; - var revealOrder = nextProps.revealOrder; - var tailMode = nextProps.tail; - var newChildren = nextProps.children; - validateRevealOrder(revealOrder); - validateTailOptions(tailMode, revealOrder); - validateSuspenseListChildren(newChildren, revealOrder); - reconcileChildren(current, workInProgress, newChildren, renderLanes); - var suspenseContext = suspenseStackCursor.current; - var shouldForceFallback = hasSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); + primaryChildFragment.return = workInProgress; + primaryChildFragment.sibling = null; - if (shouldForceFallback) { - suspenseContext = setShallowSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); - workInProgress.flags |= DidCapture; - } else { - var didSuspendBefore = - current !== null && (current.flags & DidCapture) !== NoFlags; + if (currentFallbackChildFragment !== null) { + // Delete the fallback child fragment + var deletions = workInProgress.deletions; - if (didSuspendBefore) { - // If we previously forced a fallback, we need to schedule work - // on any nested boundaries to let them know to try to render - // again. This is the same as context updating. - propagateSuspenseContextChange( - workInProgress, - workInProgress.child, - renderLanes - ); + if (deletions === null) { + workInProgress.deletions = [currentFallbackChildFragment]; + workInProgress.flags |= ChildDeletion; + } else { + deletions.push(currentFallbackChildFragment); } - - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); } - pushSuspenseContext(workInProgress, suspenseContext); - - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - // In legacy mode, SuspenseList doesn't work so we just - // use make it a noop by treating it as the default revealOrder. - workInProgress.memoizedState = null; - } else { - switch (revealOrder) { - case "forwards": { - var lastContentRow = findLastContentRow(workInProgress.child); - var tail; - - if (lastContentRow === null) { - // The whole list is part of the tail. - // TODO: We could fast path by just rendering the tail now. - tail = workInProgress.child; - workInProgress.child = null; - } else { - // Disconnect the tail rows after the content row. - // We're going to render them separately later. - tail = lastContentRow.sibling; - lastContentRow.sibling = null; - } + workInProgress.child = primaryChildFragment; + return primaryChildFragment; +} - initSuspenseListRenderState( - workInProgress, - false, // isBackwards - tail, - lastContentRow, - tailMode - ); - break; - } +function updateSuspenseFallbackChildren( + current, + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + var mode = workInProgress.mode; + var currentPrimaryChildFragment = current.child; + var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; + var primaryChildProps = { + mode: "hidden", + children: primaryChildren + }; + var primaryChildFragment; - case "backwards": { - // We're going to find the first row that has existing content. - // At the same time we're going to reverse the list of everything - // we pass in the meantime. That's going to be our tail in reverse - // order. - var _tail = null; - var row = workInProgress.child; - workInProgress.child = null; + if ( + // In legacy mode, we commit the primary tree as if it successfully + // completed, even though it's in an inconsistent state. + (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was + // already cloned. In legacy mode, the only case where this isn't true is + // when DevTools forces us to display a fallback; we skip the first render + // pass entirely and go straight to rendering the fallback. (In Concurrent + // Mode, SuspenseList can also trigger this scenario, but this is a legacy- + // only codepath.) + workInProgress.child !== currentPrimaryChildFragment + ) { + var progressedPrimaryFragment = workInProgress.child; + primaryChildFragment = progressedPrimaryFragment; + primaryChildFragment.childLanes = NoLanes; + primaryChildFragment.pendingProps = primaryChildProps; - while (row !== null) { - var currentRow = row.alternate; // New rows can't be content rows. + if (workInProgress.mode & ProfileMode) { + // Reset the durations from the first pass so they aren't included in the + // final amounts. This seems counterintuitive, since we're intentionally + // not measuring part of the render phase, but this makes it match what we + // do in Concurrent Mode. + primaryChildFragment.actualDuration = 0; + primaryChildFragment.actualStartTime = -1; + primaryChildFragment.selfBaseDuration = + currentPrimaryChildFragment.selfBaseDuration; + primaryChildFragment.treeBaseDuration = + currentPrimaryChildFragment.treeBaseDuration; + } // The fallback fiber was added as a deletion during the first pass. + // However, since we're going to remain on the fallback, we no longer want + // to delete it. - if (currentRow !== null && findFirstSuspended(currentRow) === null) { - // This is the beginning of the main content. - workInProgress.child = row; - break; - } + workInProgress.deletions = null; + } else { + primaryChildFragment = createWorkInProgressOffscreenFiber( + currentPrimaryChildFragment, + primaryChildProps + ); // Since we're reusing a current tree, we need to reuse the flags, too. + // (We don't do this in legacy mode, because in legacy mode we don't re-use + // the current tree; see previous branch.) - var nextRow = row.sibling; - row.sibling = _tail; - _tail = row; - row = nextRow; - } // TODO: If workInProgress.child is null, we can continue on the tail immediately. + primaryChildFragment.subtreeFlags = + currentPrimaryChildFragment.subtreeFlags & StaticMask; + } - initSuspenseListRenderState( - workInProgress, - true, // isBackwards - _tail, - null, // last - tailMode - ); - break; - } + var fallbackChildFragment; - case "together": { - initSuspenseListRenderState( - workInProgress, - false, // isBackwards - null, // tail - null, // last - undefined - ); - break; - } + if (currentFallbackChildFragment !== null) { + fallbackChildFragment = createWorkInProgress( + currentFallbackChildFragment, + fallbackChildren + ); + } else { + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); // Needs a placement effect because the parent (the Suspense boundary) already + // mounted but this is a new fiber. - default: { - // The default reveal order is the same as not having - // a boundary. - workInProgress.memoizedState = null; - } - } + fallbackChildFragment.flags |= Placement; } - return workInProgress.child; + fallbackChildFragment.return = workInProgress; + primaryChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; } -function updatePortalComponent(current, workInProgress, renderLanes) { - pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); - var nextChildren = workInProgress.pendingProps; +function scheduleWorkOnFiber(fiber, renderLanes) { + fiber.lanes = mergeLanes(fiber.lanes, renderLanes); + var alternate = fiber.alternate; - if (current === null) { - // Portals are special because we don't append the children during mount - // but at commit. Therefore we need to track insertions which the normal - // flow doesn't do during mount. This doesn't happen at the root because - // the root always starts with a "current" with a null child. - // TODO: Consider unifying this with how the root works. - workInProgress.child = reconcileChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - } else { - reconcileChildren(current, workInProgress, nextChildren, renderLanes); + if (alternate !== null) { + alternate.lanes = mergeLanes(alternate.lanes, renderLanes); } - return workInProgress.child; + scheduleWorkOnParentPath(fiber.return, renderLanes); } -var hasWarnedAboutUsingNoValuePropOnContextProvider = false; - -function updateContextProvider(current, workInProgress, renderLanes) { - var providerType = workInProgress.type; - var context = providerType._context; - var newProps = workInProgress.pendingProps; - var oldProps = workInProgress.memoizedProps; - var newValue = newProps.value; +function propagateSuspenseContextChange( + workInProgress, + firstChild, + renderLanes +) { + // Mark any Suspense boundaries with fallbacks as having work to do. + // If they were previously forced into fallbacks, they may now be able + // to unblock. + var node = firstChild; - { - if (!("value" in newProps)) { - if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { - hasWarnedAboutUsingNoValuePropOnContextProvider = true; + while (node !== null) { + if (node.tag === SuspenseComponent) { + var state = node.memoizedState; - error( - "The `value` prop is required for the ``. Did you misspell it or forget to pass it?" - ); + if (state !== null) { + scheduleWorkOnFiber(node, renderLanes); } + } else if (node.tag === SuspenseListComponent) { + // If the tail is hidden there might not be an Suspense boundaries + // to schedule work on. In this case we have to schedule it on the + // list itself. + // We don't have to traverse to the children of the list since + // the list will propagate the change when it rerenders. + scheduleWorkOnFiber(node, renderLanes); + } else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; } - var providerPropTypes = workInProgress.type.propTypes; + if (node === workInProgress) { + return; + } - if (providerPropTypes) { - checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider"); + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; + } + + node = node.return; } + + node.sibling.return = node.return; + node = node.sibling; } +} - pushProvider(workInProgress, context, newValue); +function findLastContentRow(firstChild) { + // This is going to find the last row among these children that is already + // showing content on the screen, as opposed to being in fallback state or + // new. If a row has multiple Suspense boundaries, any of them being in the + // fallback state, counts as the whole row being in a fallback state. + // Note that the "rows" will be workInProgress, but any nested children + // will still be current since we haven't rendered them yet. The mounted + // order may not be the same as the new order. We use the new order. + var row = firstChild; + var lastContentRow = null; - { - if (oldProps !== null) { - var oldValue = oldProps.value; + while (row !== null) { + var currentRow = row.alternate; // New rows can't be content rows. - if (objectIs(oldValue, newValue)) { - // No change. Bailout early if children are the same. - if (oldProps.children === newProps.children && !hasContextChanged()) { - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); - } - } else { - // The context value changed. Search for matching consumers and schedule - // them to update. - propagateContextChange(workInProgress, context, renderLanes); - } + if (currentRow !== null && findFirstSuspended(currentRow) === null) { + lastContentRow = row; } + + row = row.sibling; } - var newChildren = newProps.children; - reconcileChildren(current, workInProgress, newChildren, renderLanes); - return workInProgress.child; + return lastContentRow; } -var hasWarnedAboutUsingContextAsConsumer = false; +function validateRevealOrder(revealOrder) { + { + if ( + revealOrder !== undefined && + revealOrder !== "forwards" && + revealOrder !== "backwards" && + revealOrder !== "together" && + !didWarnAboutRevealOrder[revealOrder] + ) { + didWarnAboutRevealOrder[revealOrder] = true; -function updateContextConsumer(current, workInProgress, renderLanes) { - var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In - // DEV mode, we create a separate object for Context.Consumer that acts - // like a proxy to Context. This proxy object adds unnecessary code in PROD - // so we use the old behaviour (Context.Consumer references Context) to - // reduce size and overhead. The separate object references context via - // a property called "_context", which also gives us the ability to check - // in DEV mode if this property exists or not and warn if it does not. + if (typeof revealOrder === "string") { + switch (revealOrder.toLowerCase()) { + case "together": + case "forwards": + case "backwards": { + error( + '"%s" is not a valid value for revealOrder on . ' + + 'Use lowercase "%s" instead.', + revealOrder, + revealOrder.toLowerCase() + ); - { - if (context._context === undefined) { - // This may be because it's a Context (rather than a Consumer). - // Or it may be because it's older React where they're the same thing. - // We only want to warn if we're sure it's a new React. - if (context !== context.Consumer) { - if (!hasWarnedAboutUsingContextAsConsumer) { - hasWarnedAboutUsingContextAsConsumer = true; + break; + } - error( - "Rendering directly is not supported and will be removed in " + - "a future major release. Did you mean to render instead?" - ); - } - } - } else { - context = context._context; - } - } + case "forward": + case "backward": { + error( + '"%s" is not a valid value for revealOrder on . ' + + 'React uses the -s suffix in the spelling. Use "%ss" instead.', + revealOrder, + revealOrder.toLowerCase() + ); - var newProps = workInProgress.pendingProps; - var render = newProps.children; + break; + } - { - if (typeof render !== "function") { - error( - "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." - ); + default: + error( + '"%s" is not a supported revealOrder on . ' + + 'Did you mean "together", "forwards" or "backwards"?', + revealOrder + ); + + break; + } + } else { + error( + "%s is not a supported value for revealOrder on . " + + 'Did you mean "together", "forwards" or "backwards"?', + revealOrder + ); + } } } +} - prepareToReadContext(workInProgress, renderLanes); - var newValue = readContext(context); - var newChildren; - +function validateTailOptions(tailMode, revealOrder) { { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - newChildren = render(newValue); - setIsRendering(false); - } // React DevTools reads this flag. - - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, newChildren, renderLanes); - return workInProgress.child; -} + if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { + if (tailMode !== "collapsed" && tailMode !== "hidden") { + didWarnAboutTailOptions[tailMode] = true; -function markWorkInProgressReceivedUpdate() { - didReceiveUpdate = true; -} + error( + '"%s" is not a supported value for tail on . ' + + 'Did you mean "collapsed" or "hidden"?', + tailMode + ); + } else if (revealOrder !== "forwards" && revealOrder !== "backwards") { + didWarnAboutTailOptions[tailMode] = true; -function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { - if (current !== null) { - // Reuse previous dependencies - workInProgress.dependencies = current.dependencies; + error( + ' is only valid if revealOrder is ' + + '"forwards" or "backwards". ' + + 'Did you mean to specify revealOrder="forwards"?', + tailMode + ); + } + } } +} +function validateSuspenseListNestedChild(childSlot, index) { { - // Don't update "base" render times for bailouts. - stopProfilerTimerIfRunning(); - } + var isAnArray = isArray(childSlot); + var isIterable = + !isAnArray && typeof getIteratorFn(childSlot) === "function"; - markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. + if (isAnArray || isIterable) { + var type = isAnArray ? "array" : "iterable"; - if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { - // The children don't have any work either. We can skip them. - // TODO: Once we add back resuming, we should check if the children are - // a work-in-progress set. If so, we need to transfer their effects. - { - return null; + error( + "A nested %s was passed to row #%s in . Wrap it in " + + "an additional SuspenseList to configure its revealOrder: " + + " ... " + + "{%s} ... " + + "", + type, + index, + type + ); + + return false; } - } // This fiber doesn't have work, but its subtree does. Clone the child - // fibers and continue. + } - cloneChildFibers(current, workInProgress); - return workInProgress.child; + return true; } -function remountFiber(current, oldWorkInProgress, newWorkInProgress) { +function validateSuspenseListChildren(children, revealOrder) { { - var returnFiber = oldWorkInProgress.return; - - if (returnFiber === null) { - throw new Error("Cannot swap the root fiber."); - } // Disconnect from the old current. - // It will get deleted. - - current.alternate = null; - oldWorkInProgress.alternate = null; // Connect to the new tree. - - newWorkInProgress.index = oldWorkInProgress.index; - newWorkInProgress.sibling = oldWorkInProgress.sibling; - newWorkInProgress.return = oldWorkInProgress.return; - newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. + if ( + (revealOrder === "forwards" || revealOrder === "backwards") && + children !== undefined && + children !== null && + children !== false + ) { + if (isArray(children)) { + for (var i = 0; i < children.length; i++) { + if (!validateSuspenseListNestedChild(children[i], i)) { + return; + } + } + } else { + var iteratorFn = getIteratorFn(children); - if (oldWorkInProgress === returnFiber.child) { - returnFiber.child = newWorkInProgress; - } else { - var prevSibling = returnFiber.child; + if (typeof iteratorFn === "function") { + var childrenIterator = iteratorFn.call(children); - if (prevSibling === null) { - throw new Error("Expected parent to have a child."); - } + if (childrenIterator) { + var step = childrenIterator.next(); + var _i = 0; - while (prevSibling.sibling !== oldWorkInProgress) { - prevSibling = prevSibling.sibling; + for (; !step.done; step = childrenIterator.next()) { + if (!validateSuspenseListNestedChild(step.value, _i)) { + return; + } - if (prevSibling === null) { - throw new Error("Expected to find the previous sibling."); + _i++; + } + } + } else { + error( + 'A single row was passed to a . ' + + "This is not useful since it needs multiple rows. " + + "Did you mean to pass multiple children or an array?", + revealOrder + ); } } - - prevSibling.sibling = newWorkInProgress; - } // Delete the old fiber and place the new one. - // Since the old fiber is disconnected, we have to schedule it manually. - - var deletions = returnFiber.deletions; - - if (deletions === null) { - returnFiber.deletions = [current]; - returnFiber.flags |= ChildDeletion; - } else { - deletions.push(current); } - - newWorkInProgress.flags |= Placement; // Restart work from the new fiber. - - return newWorkInProgress; } } -function beginWork(current, workInProgress, renderLanes) { - var updateLanes = workInProgress.lanes; +function initSuspenseListRenderState( + workInProgress, + isBackwards, + tail, + lastContentRow, + tailMode +) { + var renderState = workInProgress.memoizedState; - { - if (workInProgress._debugNeedsRemount && current !== null) { - // This will restart the begin phase with a new fiber. - return remountFiber( - current, - workInProgress, - createFiberFromTypeAndProps( - workInProgress.type, - workInProgress.key, - workInProgress.pendingProps, - workInProgress._debugOwner || null, - workInProgress.mode, - workInProgress.lanes - ) - ); - } + if (renderState === null) { + workInProgress.memoizedState = { + isBackwards: isBackwards, + rendering: null, + renderingStartTime: 0, + last: lastContentRow, + tail: tail, + tailMode: tailMode + }; + } else { + // We can reuse the existing object from previous renders. + renderState.isBackwards = isBackwards; + renderState.rendering = null; + renderState.renderingStartTime = 0; + renderState.last = lastContentRow; + renderState.tail = tail; + renderState.tailMode = tailMode; } +} // This can end up rendering this component multiple passes. +// The first pass splits the children fibers into two sets. A head and tail. +// We first render the head. If anything is in fallback state, we do another +// pass through beginWork to rerender all children (including the tail) with +// the force suspend context. If the first render didn't have anything in +// in fallback state. Then we render each row in the tail one-by-one. +// That happens in the completeWork phase without going back to beginWork. - if (current !== null) { - var oldProps = current.memoizedProps; - var newProps = workInProgress.pendingProps; - - if ( - oldProps !== newProps || - hasContextChanged() || // Force a re-render if the implementation changed due to hot reload: - workInProgress.type !== current.type - ) { - // If props or context changed, mark the fiber as having performed work. - // This may be unset if the props are determined to be equal later (memo). - didReceiveUpdate = true; - } else if (!includesSomeLane(renderLanes, updateLanes)) { - didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering - // the begin phase. There's still some bookkeeping we that needs to be done - // in this optimized path, mostly pushing stuff onto the stack. - - switch (workInProgress.tag) { - case HostRoot: - pushHostRootContext(workInProgress); - break; +function updateSuspenseListComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; + var revealOrder = nextProps.revealOrder; + var tailMode = nextProps.tail; + var newChildren = nextProps.children; + validateRevealOrder(revealOrder); + validateTailOptions(tailMode, revealOrder); + validateSuspenseListChildren(newChildren, revealOrder); + reconcileChildren(current, workInProgress, newChildren, renderLanes); + var suspenseContext = suspenseStackCursor.current; + var shouldForceFallback = hasSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); - case HostComponent: - pushHostContext(workInProgress); - break; + if (shouldForceFallback) { + suspenseContext = setShallowSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + workInProgress.flags |= DidCapture; + } else { + var didSuspendBefore = + current !== null && (current.flags & DidCapture) !== NoFlags; - case ClassComponent: { - var Component = workInProgress.type; + if (didSuspendBefore) { + // If we previously forced a fallback, we need to schedule work + // on any nested boundaries to let them know to try to render + // again. This is the same as context updating. + propagateSuspenseContextChange( + workInProgress, + workInProgress.child, + renderLanes + ); + } - if (isContextProvider(Component)) { - pushContextProvider(workInProgress); - } + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + } - break; - } + pushSuspenseContext(workInProgress, suspenseContext); - case HostPortal: - pushHostContainer( - workInProgress, - workInProgress.stateNode.containerInfo - ); - break; + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + // In legacy mode, SuspenseList doesn't work so we just + // use make it a noop by treating it as the default revealOrder. + workInProgress.memoizedState = null; + } else { + switch (revealOrder) { + case "forwards": { + var lastContentRow = findLastContentRow(workInProgress.child); + var tail; - case ContextProvider: { - var newValue = workInProgress.memoizedProps.value; - var context = workInProgress.type._context; - pushProvider(workInProgress, context, newValue); - break; + if (lastContentRow === null) { + // The whole list is part of the tail. + // TODO: We could fast path by just rendering the tail now. + tail = workInProgress.child; + workInProgress.child = null; + } else { + // Disconnect the tail rows after the content row. + // We're going to render them separately later. + tail = lastContentRow.sibling; + lastContentRow.sibling = null; } - case Profiler: - { - // Profiler should only call onRender when one of its descendants actually rendered. - var hasChildWork = includesSomeLane( - renderLanes, - workInProgress.childLanes - ); + initSuspenseListRenderState( + workInProgress, + false, // isBackwards + tail, + lastContentRow, + tailMode + ); + break; + } - if (hasChildWork) { - workInProgress.flags |= Update; - } + case "backwards": { + // We're going to find the first row that has existing content. + // At the same time we're going to reverse the list of everything + // we pass in the meantime. That's going to be our tail in reverse + // order. + var _tail = null; + var row = workInProgress.child; + workInProgress.child = null; + + while (row !== null) { + var currentRow = row.alternate; // New rows can't be content rows. + + if (currentRow !== null && findFirstSuspended(currentRow) === null) { + // This is the beginning of the main content. + workInProgress.child = row; + break; } - break; + var nextRow = row.sibling; + row.sibling = _tail; + _tail = row; + row = nextRow; + } // TODO: If workInProgress.child is null, we can continue on the tail immediately. - case SuspenseComponent: { - var state = workInProgress.memoizedState; + initSuspenseListRenderState( + workInProgress, + true, // isBackwards + _tail, + null, // last + tailMode + ); + break; + } - if (state !== null) { - // whether to retry the primary children, or to skip over it and - // go straight to the fallback. Check the priority of the primary - // child fragment. + case "together": { + initSuspenseListRenderState( + workInProgress, + false, // isBackwards + null, // tail + null, // last + undefined + ); + break; + } - var primaryChildFragment = workInProgress.child; - var primaryChildLanes = primaryChildFragment.childLanes; + default: { + // The default reveal order is the same as not having + // a boundary. + workInProgress.memoizedState = null; + } + } + } - if (includesSomeLane(renderLanes, primaryChildLanes)) { - // The primary children have pending work. Use the normal path - // to attempt to render the primary children again. - return updateSuspenseComponent( - current, - workInProgress, - renderLanes - ); - } else { - // The primary child fragment does not have pending work marked - // on it - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ); // The primary children do not have pending work with sufficient - // priority. Bailout. + return workInProgress.child; +} - var child = bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); +function updatePortalComponent(current, workInProgress, renderLanes) { + pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); + var nextChildren = workInProgress.pendingProps; - if (child !== null) { - // The fallback children have pending work. Skip over the - // primary children and work on the fallback. - return child.sibling; - } else { - // Note: We can return `null` here because we already checked - // whether there were nested context consumers, via the call to - // `bailoutOnAlreadyFinishedWork` above. - return null; - } - } - } else { - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ); - } + if (current === null) { + // Portals are special because we don't append the children during mount + // but at commit. Therefore we need to track insertions which the normal + // flow doesn't do during mount. This doesn't happen at the root because + // the root always starts with a "current" with a null child. + // TODO: Consider unifying this with how the root works. + workInProgress.child = reconcileChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); + } else { + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } - break; - } + return workInProgress.child; +} - case SuspenseListComponent: { - var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; +var hasWarnedAboutUsingNoValuePropOnContextProvider = false; - var _hasChildWork = includesSomeLane( - renderLanes, - workInProgress.childLanes - ); +function updateContextProvider(current, workInProgress, renderLanes) { + var providerType = workInProgress.type; + var context = providerType._context; + var newProps = workInProgress.pendingProps; + var oldProps = workInProgress.memoizedProps; + var newValue = newProps.value; - if (didSuspendBefore) { - if (_hasChildWork) { - // If something was in fallback state last time, and we have all the - // same children then we're still in progressive loading state. - // Something might get unblocked by state updates or retries in the - // tree which will affect the tail. So we need to use the normal - // path to compute the correct tail. - return updateSuspenseListComponent( - current, - workInProgress, - renderLanes - ); - } // If none of the children had any work, that means that none of - // them got retried so they'll still be blocked in the same way - // as before. We can fast bail out. + { + if (!("value" in newProps)) { + if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { + hasWarnedAboutUsingNoValuePropOnContextProvider = true; - workInProgress.flags |= DidCapture; - } // If nothing suspended before and we're rendering the same children, - // then the tail doesn't matter. Anything new that suspends will work - // in the "together" mode, so we can continue from the state we had. + error( + "The `value` prop is required for the ``. Did you misspell it or forget to pass it?" + ); + } + } - var renderState = workInProgress.memoizedState; + var providerPropTypes = workInProgress.type.propTypes; - if (renderState !== null) { - // Reset to the "together" mode in case we've started a different - // update in the past but didn't complete it. - renderState.rendering = null; - renderState.tail = null; - renderState.lastEffect = null; - } + if (providerPropTypes) { + checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider"); + } + } - pushSuspenseContext(workInProgress, suspenseStackCursor.current); + pushProvider(workInProgress, context, newValue); - if (_hasChildWork) { - break; - } else { - // If none of the children had any work, that means that none of - // them got retried so they'll still be blocked in the same way - // as before. We can fast bail out. - return null; - } - } + { + if (oldProps !== null) { + var oldValue = oldProps.value; - case OffscreenComponent: - case LegacyHiddenComponent: { - // Need to check if the tree still needs to be deferred. This is - // almost identical to the logic used in the normal update path, - // so we'll just enter that. The only difference is we'll bail out - // at the next level instead of this one, because the child props - // have not changed. Which is fine. - // TODO: Probably should refactor `beginWork` to split the bailout - // path from the normal path. I'm tempted to do a labeled break here - // but I won't :) - workInProgress.lanes = NoLanes; - return updateOffscreenComponent(current, workInProgress, renderLanes); + if (objectIs(oldValue, newValue)) { + // No change. Bailout early if children are the same. + if (oldProps.children === newProps.children && !hasContextChanged()) { + return bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); } - } - - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } else { - if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { - // This is a special case that only exists for legacy mode. - // See https://github.com/facebook/react/pull/19216. - didReceiveUpdate = true; } else { - // An update was scheduled on this fiber, but there are no new props - // nor legacy context. Set this to false. If an update queue or context - // consumer produces a changed value, it will set this to true. Otherwise, - // the component will assume the children have not changed and bail out. - didReceiveUpdate = false; + // The context value changed. Search for matching consumers and schedule + // them to update. + propagateContextChange(workInProgress, context, renderLanes); } } - } else { - didReceiveUpdate = false; - } // Before entering the begin phase, clear pending update priority. - // TODO: This assumes that we're about to evaluate the component and process - // the update queue. However, there's an exception: SimpleMemoComponent - // sometimes bails out later in the begin phase. This indicates that we should - // move this assignment out of the common path and into each branch. + } - workInProgress.lanes = NoLanes; + var newChildren = newProps.children; + reconcileChildren(current, workInProgress, newChildren, renderLanes); + return workInProgress.child; +} - switch (workInProgress.tag) { - case IndeterminateComponent: { - return mountIndeterminateComponent( - current, - workInProgress, - workInProgress.type, - renderLanes - ); - } +var hasWarnedAboutUsingContextAsConsumer = false; - case LazyComponent: { - var elementType = workInProgress.elementType; - return mountLazyComponent( - current, - workInProgress, - elementType, - updateLanes, - renderLanes - ); - } +function updateContextConsumer(current, workInProgress, renderLanes) { + var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In + // DEV mode, we create a separate object for Context.Consumer that acts + // like a proxy to Context. This proxy object adds unnecessary code in PROD + // so we use the old behaviour (Context.Consumer references Context) to + // reduce size and overhead. The separate object references context via + // a property called "_context", which also gives us the ability to check + // in DEV mode if this property exists or not and warn if it does not. - case FunctionComponent: { - var _Component = workInProgress.type; - var unresolvedProps = workInProgress.pendingProps; - var resolvedProps = - workInProgress.elementType === _Component - ? unresolvedProps - : resolveDefaultProps(_Component, unresolvedProps); - return updateFunctionComponent( - current, - workInProgress, - _Component, - resolvedProps, - renderLanes - ); - } + { + if (context._context === undefined) { + // This may be because it's a Context (rather than a Consumer). + // Or it may be because it's older React where they're the same thing. + // We only want to warn if we're sure it's a new React. + if (context !== context.Consumer) { + if (!hasWarnedAboutUsingContextAsConsumer) { + hasWarnedAboutUsingContextAsConsumer = true; - case ClassComponent: { - var _Component2 = workInProgress.type; - var _unresolvedProps = workInProgress.pendingProps; + error( + "Rendering directly is not supported and will be removed in " + + "a future major release. Did you mean to render instead?" + ); + } + } + } else { + context = context._context; + } + } - var _resolvedProps = - workInProgress.elementType === _Component2 - ? _unresolvedProps - : resolveDefaultProps(_Component2, _unresolvedProps); + var newProps = workInProgress.pendingProps; + var render = newProps.children; - return updateClassComponent( - current, - workInProgress, - _Component2, - _resolvedProps, - renderLanes + { + if (typeof render !== "function") { + error( + "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." ); } + } - case HostRoot: - return updateHostRoot(current, workInProgress, renderLanes); + prepareToReadContext(workInProgress, renderLanes); + var newValue = readContext(context); + var newChildren; - case HostComponent: - return updateHostComponent(current, workInProgress, renderLanes); + { + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + newChildren = render(newValue); + setIsRendering(false); + } // React DevTools reads this flag. - case HostText: - return updateHostText(); + workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, newChildren, renderLanes); + return workInProgress.child; +} - case SuspenseComponent: - return updateSuspenseComponent(current, workInProgress, renderLanes); +function markWorkInProgressReceivedUpdate() { + didReceiveUpdate = true; +} - case HostPortal: - return updatePortalComponent(current, workInProgress, renderLanes); +function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { + if (current !== null) { + // Reuse previous dependencies + workInProgress.dependencies = current.dependencies; + } - case ForwardRef: { - var type = workInProgress.type; - var _unresolvedProps2 = workInProgress.pendingProps; + { + // Don't update "base" render times for bailouts. + stopProfilerTimerIfRunning(); + } - var _resolvedProps2 = - workInProgress.elementType === type - ? _unresolvedProps2 - : resolveDefaultProps(type, _unresolvedProps2); + markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. - return updateForwardRef( - current, - workInProgress, - type, - _resolvedProps2, - renderLanes - ); + if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { + // The children don't have any work either. We can skip them. + // TODO: Once we add back resuming, we should check if the children are + // a work-in-progress set. If so, we need to transfer their effects. + { + return null; } + } // This fiber doesn't have work, but its subtree does. Clone the child + // fibers and continue. - case Fragment: - return updateFragment(current, workInProgress, renderLanes); + cloneChildFibers(current, workInProgress); + return workInProgress.child; +} - case Mode: - return updateMode(current, workInProgress, renderLanes); +function remountFiber(current, oldWorkInProgress, newWorkInProgress) { + { + var returnFiber = oldWorkInProgress.return; - case Profiler: - return updateProfiler(current, workInProgress, renderLanes); + if (returnFiber === null) { + throw new Error("Cannot swap the root fiber."); + } // Disconnect from the old current. + // It will get deleted. - case ContextProvider: - return updateContextProvider(current, workInProgress, renderLanes); + current.alternate = null; + oldWorkInProgress.alternate = null; // Connect to the new tree. - case ContextConsumer: - return updateContextConsumer(current, workInProgress, renderLanes); + newWorkInProgress.index = oldWorkInProgress.index; + newWorkInProgress.sibling = oldWorkInProgress.sibling; + newWorkInProgress.return = oldWorkInProgress.return; + newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. - case MemoComponent: { - var _type2 = workInProgress.type; - var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. + if (oldWorkInProgress === returnFiber.child) { + returnFiber.child = newWorkInProgress; + } else { + var prevSibling = returnFiber.child; - var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); + if (prevSibling === null) { + throw new Error("Expected parent to have a child."); + } - { - if (workInProgress.type !== workInProgress.elementType) { - var outerPropTypes = _type2.propTypes; + while (prevSibling.sibling !== oldWorkInProgress) { + prevSibling = prevSibling.sibling; - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - _resolvedProps3, // Resolved for outer only - "prop", - getComponentNameFromType(_type2) - ); - } + if (prevSibling === null) { + throw new Error("Expected to find the previous sibling."); } } - _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); - return updateMemoComponent( - current, - workInProgress, - _type2, - _resolvedProps3, - updateLanes, - renderLanes - ); - } - - case SimpleMemoComponent: { - return updateSimpleMemoComponent( - current, - workInProgress, - workInProgress.type, - workInProgress.pendingProps, - updateLanes, - renderLanes - ); - } - - case IncompleteClassComponent: { - var _Component3 = workInProgress.type; - var _unresolvedProps4 = workInProgress.pendingProps; - - var _resolvedProps4 = - workInProgress.elementType === _Component3 - ? _unresolvedProps4 - : resolveDefaultProps(_Component3, _unresolvedProps4); - - return mountIncompleteClassComponent( - current, - workInProgress, - _Component3, - _resolvedProps4, - renderLanes - ); - } - - case SuspenseListComponent: { - return updateSuspenseListComponent(current, workInProgress, renderLanes); - } + prevSibling.sibling = newWorkInProgress; + } // Delete the old fiber and place the new one. + // Since the old fiber is disconnected, we have to schedule it manually. - case ScopeComponent: { - break; - } + var deletions = returnFiber.deletions; - case OffscreenComponent: { - return updateOffscreenComponent(current, workInProgress, renderLanes); + if (deletions === null) { + returnFiber.deletions = [current]; + returnFiber.flags |= ChildDeletion; + } else { + deletions.push(current); } - case LegacyHiddenComponent: { - return updateLegacyHiddenComponent(current, workInProgress, renderLanes); - } - } + newWorkInProgress.flags |= Placement; // Restart work from the new fiber. - { - throw Error( - "Unknown unit of work tag (" + - workInProgress.tag + - "). This error is likely caused by a bug in React. Please file an issue." - ); + return newWorkInProgress; } } -function markUpdate(workInProgress) { - // Tag the fiber with an update effect. This turns a Placement into - // a PlacementAndUpdate. - workInProgress.flags |= Update; -} - -function markRef$1(workInProgress) { - workInProgress.flags |= Ref; -} - -function hadNoMutationsEffects(current, completedWork) { - var didBailout = current !== null && current.child === completedWork.child; +function beginWork(current, workInProgress, renderLanes) { + var updateLanes = workInProgress.lanes; - if (didBailout) { - return true; + { + if (workInProgress._debugNeedsRemount && current !== null) { + // This will restart the begin phase with a new fiber. + return remountFiber( + current, + workInProgress, + createFiberFromTypeAndProps( + workInProgress.type, + workInProgress.key, + workInProgress.pendingProps, + workInProgress._debugOwner || null, + workInProgress.mode, + workInProgress.lanes + ) + ); + } } - if ((completedWork.flags & ChildDeletion) !== NoFlags) { - return false; - } // TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties` - // then we only have to check the `completedWork.subtreeFlags`. - - var child = completedWork.child; + if (current !== null) { + var oldProps = current.memoizedProps; + var newProps = workInProgress.pendingProps; - while (child !== null) { if ( - (child.flags & MutationMask) !== NoFlags || - (child.subtreeFlags & MutationMask) !== NoFlags + oldProps !== newProps || + hasContextChanged() || // Force a re-render if the implementation changed due to hot reload: + workInProgress.type !== current.type ) { - return false; - } - - child = child.sibling; - } + // If props or context changed, mark the fiber as having performed work. + // This may be unset if the props are determined to be equal later (memo). + didReceiveUpdate = true; + } else if (!includesSomeLane(renderLanes, updateLanes)) { + didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering + // the begin phase. There's still some bookkeeping we that needs to be done + // in this optimized path, mostly pushing stuff onto the stack. - return true; -} + switch (workInProgress.tag) { + case HostRoot: + pushHostRootContext(workInProgress); + break; -var appendAllChildren; -var updateHostContainer; -var updateHostComponent$1; -var updateHostText$1; + case HostComponent: + pushHostContext(workInProgress); + break; -{ - // Persistent host tree mode - appendAllChildren = function( - parent, - workInProgress, - needsVisibilityToggle, - isHidden - ) { - // We only have the top Fiber that was created but we need recurse down its - // children to find all the terminal nodes. - var node = workInProgress.child; + case ClassComponent: { + var Component = workInProgress.type; - while (node !== null) { - // eslint-disable-next-line no-labels - if (node.tag === HostComponent) { - var instance = node.stateNode; + if (isContextProvider(Component)) { + pushContextProvider(workInProgress); + } - if (needsVisibilityToggle && isHidden) { - // This child is inside a timed out tree. Hide it. - var props = node.memoizedProps; - var type = node.type; - instance = cloneHiddenInstance(instance); + break; } - appendInitialChild(parent, instance); - } else if (node.tag === HostText) { - var _instance = node.stateNode; + case HostPortal: + pushHostContainer( + workInProgress, + workInProgress.stateNode.containerInfo + ); + break; - if (needsVisibilityToggle && isHidden) { - // This child is inside a timed out tree. Hide it. - var text = node.memoizedProps; - _instance = cloneHiddenTextInstance(); + case ContextProvider: { + var newValue = workInProgress.memoizedProps.value; + var context = workInProgress.type._context; + pushProvider(workInProgress, context, newValue); + break; } - appendInitialChild(parent, _instance); - } else if (node.tag === HostPortal); - else if (node.tag === SuspenseComponent) { - if ((node.flags & Update) !== NoFlags) { - // Need to toggle the visibility of the primary children. - var newIsHidden = node.memoizedState !== null; - - if (newIsHidden) { - var primaryChildParent = node.child; - - if (primaryChildParent !== null) { - if (primaryChildParent.child !== null) { - primaryChildParent.child.return = primaryChildParent; - appendAllChildren( - parent, - primaryChildParent, - true, - newIsHidden - ); - } + case Profiler: + { + // Profiler should only call onRender when one of its descendants actually rendered. + var hasChildWork = includesSomeLane( + renderLanes, + workInProgress.childLanes + ); - var fallbackChildParent = primaryChildParent.sibling; + if (hasChildWork) { + workInProgress.flags |= Update; + } - if (fallbackChildParent !== null) { - fallbackChildParent.return = node; - node = fallbackChildParent; - continue; - } + { + // Reset effect durations for the next eventual effect phase. + // These are reset during render to allow the DevTools commit hook a chance to read them, + var stateNode = workInProgress.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; } } - } - - if (node.child !== null) { - // Continue traversing like normal - node.child.return = node; - node = node.child; - continue; - } - } else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } // $FlowFixMe This is correct but Flow is confused by the labeled break. - - node = node; - if (node === workInProgress) { - return; - } + break; - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; - } + case SuspenseComponent: { + var state = workInProgress.memoizedState; - node = node.return; - } + if (state !== null) { + // whether to retry the primary children, or to skip over it and + // go straight to the fallback. Check the priority of the primary + // child fragment. - node.sibling.return = node.return; - node = node.sibling; - } - }; // An unfortunate fork of appendAllChildren because we have two different parent types. + var primaryChildFragment = workInProgress.child; + var primaryChildLanes = primaryChildFragment.childLanes; - var appendAllChildrenToContainer = function( - containerChildSet, - workInProgress, - needsVisibilityToggle, - isHidden - ) { - // We only have the top Fiber that was created but we need recurse down its - // children to find all the terminal nodes. - var node = workInProgress.child; + if (includesSomeLane(renderLanes, primaryChildLanes)) { + // The primary children have pending work. Use the normal path + // to attempt to render the primary children again. + return updateSuspenseComponent( + current, + workInProgress, + renderLanes + ); + } else { + // The primary child fragment does not have pending work marked + // on it + pushSuspenseContext( + workInProgress, + setDefaultShallowSuspenseContext(suspenseStackCursor.current) + ); // The primary children do not have pending work with sufficient + // priority. Bailout. - while (node !== null) { - // eslint-disable-next-line no-labels - if (node.tag === HostComponent) { - var instance = node.stateNode; + var child = bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); - if (needsVisibilityToggle && isHidden) { - // This child is inside a timed out tree. Hide it. - var props = node.memoizedProps; - var type = node.type; - instance = cloneHiddenInstance(instance); + if (child !== null) { + // The fallback children have pending work. Skip over the + // primary children and work on the fallback. + return child.sibling; + } else { + // Note: We can return `null` here because we already checked + // whether there were nested context consumers, via the call to + // `bailoutOnAlreadyFinishedWork` above. + return null; + } + } + } else { + pushSuspenseContext( + workInProgress, + setDefaultShallowSuspenseContext(suspenseStackCursor.current) + ); + } + + break; } - appendChildToContainerChildSet(containerChildSet, instance); - } else if (node.tag === HostText) { - var _instance2 = node.stateNode; + case SuspenseListComponent: { + var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; - if (needsVisibilityToggle && isHidden) { - // This child is inside a timed out tree. Hide it. - var text = node.memoizedProps; - _instance2 = cloneHiddenTextInstance(); - } + var _hasChildWork = includesSomeLane( + renderLanes, + workInProgress.childLanes + ); - appendChildToContainerChildSet(containerChildSet, _instance2); - } else if (node.tag === HostPortal); - else if (node.tag === SuspenseComponent) { - if ((node.flags & Update) !== NoFlags) { - // Need to toggle the visibility of the primary children. - var newIsHidden = node.memoizedState !== null; + if (didSuspendBefore) { + if (_hasChildWork) { + // If something was in fallback state last time, and we have all the + // same children then we're still in progressive loading state. + // Something might get unblocked by state updates or retries in the + // tree which will affect the tail. So we need to use the normal + // path to compute the correct tail. + return updateSuspenseListComponent( + current, + workInProgress, + renderLanes + ); + } // If none of the children had any work, that means that none of + // them got retried so they'll still be blocked in the same way + // as before. We can fast bail out. - if (newIsHidden) { - var primaryChildParent = node.child; + workInProgress.flags |= DidCapture; + } // If nothing suspended before and we're rendering the same children, + // then the tail doesn't matter. Anything new that suspends will work + // in the "together" mode, so we can continue from the state we had. - if (primaryChildParent !== null) { - if (primaryChildParent.child !== null) { - primaryChildParent.child.return = primaryChildParent; - appendAllChildrenToContainer( - containerChildSet, - primaryChildParent, - true, - newIsHidden - ); - } + var renderState = workInProgress.memoizedState; - var fallbackChildParent = primaryChildParent.sibling; + if (renderState !== null) { + // Reset to the "together" mode in case we've started a different + // update in the past but didn't complete it. + renderState.rendering = null; + renderState.tail = null; + renderState.lastEffect = null; + } - if (fallbackChildParent !== null) { - fallbackChildParent.return = node; - node = fallbackChildParent; - continue; - } - } + pushSuspenseContext(workInProgress, suspenseStackCursor.current); + + if (_hasChildWork) { + break; + } else { + // If none of the children had any work, that means that none of + // them got retried so they'll still be blocked in the same way + // as before. We can fast bail out. + return null; } } - if (node.child !== null) { - // Continue traversing like normal - node.child.return = node; - node = node.child; - continue; + case OffscreenComponent: + case LegacyHiddenComponent: { + // Need to check if the tree still needs to be deferred. This is + // almost identical to the logic used in the normal update path, + // so we'll just enter that. The only difference is we'll bail out + // at the next level instead of this one, because the child props + // have not changed. Which is fine. + // TODO: Probably should refactor `beginWork` to split the bailout + // path from the normal path. I'm tempted to do a labeled break here + // but I won't :) + workInProgress.lanes = NoLanes; + return updateOffscreenComponent(current, workInProgress, renderLanes); } - } else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } // $FlowFixMe This is correct but Flow is confused by the labeled break. - - node = node; - - if (node === workInProgress) { - return; } - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; - } - - node = node.return; + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } else { + if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { + // This is a special case that only exists for legacy mode. + // See https://github.com/facebook/react/pull/19216. + didReceiveUpdate = true; + } else { + // An update was scheduled on this fiber, but there are no new props + // nor legacy context. Set this to false. If an update queue or context + // consumer produces a changed value, it will set this to true. Otherwise, + // the component will assume the children have not changed and bail out. + didReceiveUpdate = false; } - - node.sibling.return = node.return; - node = node.sibling; } - }; - - updateHostContainer = function(current, workInProgress) { - var portalOrRoot = workInProgress.stateNode; - var childrenUnchanged = hadNoMutationsEffects(current, workInProgress); - - if (childrenUnchanged); - else { - var container = portalOrRoot.containerInfo; - var newChildSet = createContainerChildSet(container); // If children might have changed, we have to add them all to the set. + } else { + didReceiveUpdate = false; + } // Before entering the begin phase, clear pending update priority. + // TODO: This assumes that we're about to evaluate the component and process + // the update queue. However, there's an exception: SimpleMemoComponent + // sometimes bails out later in the begin phase. This indicates that we should + // move this assignment out of the common path and into each branch. - appendAllChildrenToContainer(newChildSet, workInProgress, false, false); - portalOrRoot.pendingChildren = newChildSet; // Schedule an update on the container to swap out the container. + workInProgress.lanes = NoLanes; - markUpdate(workInProgress); - finalizeContainerChildren(container, newChildSet); + switch (workInProgress.tag) { + case IndeterminateComponent: { + return mountIndeterminateComponent( + current, + workInProgress, + workInProgress.type, + renderLanes + ); } - }; - - updateHostComponent$1 = function( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ) { - var currentInstance = current.stateNode; - var oldProps = current.memoizedProps; // If there are no effects associated with this node, then none of our children had any updates. - // This guarantees that we can reuse all of them. - var childrenUnchanged = hadNoMutationsEffects(current, workInProgress); + case LazyComponent: { + var elementType = workInProgress.elementType; + return mountLazyComponent( + current, + workInProgress, + elementType, + updateLanes, + renderLanes + ); + } - if (childrenUnchanged && oldProps === newProps) { - // No changes, just reuse the existing instance. - // Note that this might release a previous clone. - workInProgress.stateNode = currentInstance; - return; + case FunctionComponent: { + var _Component = workInProgress.type; + var unresolvedProps = workInProgress.pendingProps; + var resolvedProps = + workInProgress.elementType === _Component + ? unresolvedProps + : resolveDefaultProps(_Component, unresolvedProps); + return updateFunctionComponent( + current, + workInProgress, + _Component, + resolvedProps, + renderLanes + ); } - var recyclableInstance = workInProgress.stateNode; - var currentHostContext = getHostContext(); - var updatePayload = null; + case ClassComponent: { + var _Component2 = workInProgress.type; + var _unresolvedProps = workInProgress.pendingProps; - if (oldProps !== newProps) { - updatePayload = prepareUpdate( - recyclableInstance, - type, - oldProps, - newProps + var _resolvedProps = + workInProgress.elementType === _Component2 + ? _unresolvedProps + : resolveDefaultProps(_Component2, _unresolvedProps); + + return updateClassComponent( + current, + workInProgress, + _Component2, + _resolvedProps, + renderLanes ); } - if (childrenUnchanged && updatePayload === null) { - // No changes, just reuse the existing instance. - // Note that this might release a previous clone. - workInProgress.stateNode = currentInstance; - return; - } + case HostRoot: + return updateHostRoot(current, workInProgress, renderLanes); - var newInstance = cloneInstance( - currentInstance, - updatePayload, - type, - oldProps, - newProps, - workInProgress, - childrenUnchanged - ); + case HostComponent: + return updateHostComponent(current, workInProgress, renderLanes); - workInProgress.stateNode = newInstance; + case HostText: + return updateHostText(); - if (childrenUnchanged) { - // If there are no other effects in this tree, we need to flag this node as having one. - // Even though we're not going to use it for anything. - // Otherwise parents won't know that there are new children to propagate upwards. - markUpdate(workInProgress); - } else { - // If children might have changed, we have to add them all to the set. - appendAllChildren(newInstance, workInProgress, false, false); - } - }; + case SuspenseComponent: + return updateSuspenseComponent(current, workInProgress, renderLanes); - updateHostText$1 = function(current, workInProgress, oldText, newText) { - if (oldText !== newText) { - // If the text content differs, we'll create a new text instance for it. - var rootContainerInstance = getRootHostContainer(); - var currentHostContext = getHostContext(); - workInProgress.stateNode = createTextInstance( - newText, - rootContainerInstance, - currentHostContext, - workInProgress - ); // We'll have to mark it as having an effect, even though we won't use the effect for anything. - // This lets the parents know that at least one of their children has changed. + case HostPortal: + return updatePortalComponent(current, workInProgress, renderLanes); - markUpdate(workInProgress); - } else { - workInProgress.stateNode = current.stateNode; + case ForwardRef: { + var type = workInProgress.type; + var _unresolvedProps2 = workInProgress.pendingProps; + + var _resolvedProps2 = + workInProgress.elementType === type + ? _unresolvedProps2 + : resolveDefaultProps(type, _unresolvedProps2); + + return updateForwardRef( + current, + workInProgress, + type, + _resolvedProps2, + renderLanes + ); } - }; -} -function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { - switch (renderState.tailMode) { - case "hidden": { - // Any insertions at the end of the tail list after this point - // should be invisible. If there are already mounted boundaries - // anything before them are not considered for collapsing. - // Therefore we need to go through the whole tail to find if - // there are any. - var tailNode = renderState.tail; - var lastTailNode = null; + case Fragment: + return updateFragment(current, workInProgress, renderLanes); - while (tailNode !== null) { - if (tailNode.alternate !== null) { - lastTailNode = tailNode; - } + case Mode: + return updateMode(current, workInProgress, renderLanes); - tailNode = tailNode.sibling; - } // Next we're simply going to delete all insertions after the - // last rendered item. + case Profiler: + return updateProfiler(current, workInProgress, renderLanes); - if (lastTailNode === null) { - // All remaining items in the tail are insertions. - renderState.tail = null; - } else { - // Detach the insertion after the last node that was already - // inserted. - lastTailNode.sibling = null; - } + case ContextProvider: + return updateContextProvider(current, workInProgress, renderLanes); - break; - } + case ContextConsumer: + return updateContextConsumer(current, workInProgress, renderLanes); - case "collapsed": { - // Any insertions at the end of the tail list after this point - // should be invisible. If there are already mounted boundaries - // anything before them are not considered for collapsing. - // Therefore we need to go through the whole tail to find if - // there are any. - var _tailNode = renderState.tail; - var _lastTailNode = null; + case MemoComponent: { + var _type2 = workInProgress.type; + var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. - while (_tailNode !== null) { - if (_tailNode.alternate !== null) { - _lastTailNode = _tailNode; - } + var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); - _tailNode = _tailNode.sibling; - } // Next we're simply going to delete all insertions after the - // last rendered item. + { + if (workInProgress.type !== workInProgress.elementType) { + var outerPropTypes = _type2.propTypes; - if (_lastTailNode === null) { - // All remaining items in the tail are insertions. - if (!hasRenderedATailFallback && renderState.tail !== null) { - // We suspended during the head. We want to show at least one - // row at the tail. So we'll keep on and cut off the rest. - renderState.tail.sibling = null; - } else { - renderState.tail = null; + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + _resolvedProps3, // Resolved for outer only + "prop", + getComponentNameFromType(_type2) + ); + } } - } else { - // Detach the insertion after the last node that was already - // inserted. - _lastTailNode.sibling = null; } - break; + _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); + return updateMemoComponent( + current, + workInProgress, + _type2, + _resolvedProps3, + updateLanes, + renderLanes + ); } - } -} - -function bubbleProperties(completedWork) { - var didBailout = - completedWork.alternate !== null && - completedWork.alternate.child === completedWork.child; - var newChildLanes = NoLanes; - var subtreeFlags = NoFlags; - - if (!didBailout) { - // Bubble up the earliest expiration time. - if ((completedWork.mode & ProfileMode) !== NoMode) { - // In profiling mode, resetChildExpirationTime is also used to reset - // profiler durations. - var actualDuration = completedWork.actualDuration; - var treeBaseDuration = completedWork.selfBaseDuration; - var child = completedWork.child; - while (child !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(child.lanes, child.childLanes) - ); - subtreeFlags |= child.subtreeFlags; - subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will - // only be updated if work is done on the fiber (i.e. it doesn't bailout). - // When work is done, it should bubble to the parent's actualDuration. If - // the fiber has not been cloned though, (meaning no work was done), then - // this value will reflect the amount of time spent working on a previous - // render. In that case it should not bubble. We determine whether it was - // cloned by comparing the child pointer. + case SimpleMemoComponent: { + return updateSimpleMemoComponent( + current, + workInProgress, + workInProgress.type, + workInProgress.pendingProps, + updateLanes, + renderLanes + ); + } - actualDuration += child.actualDuration; - treeBaseDuration += child.treeBaseDuration; - child = child.sibling; - } + case IncompleteClassComponent: { + var _Component3 = workInProgress.type; + var _unresolvedProps4 = workInProgress.pendingProps; - completedWork.actualDuration = actualDuration; - completedWork.treeBaseDuration = treeBaseDuration; - } else { - var _child = completedWork.child; + var _resolvedProps4 = + workInProgress.elementType === _Component3 + ? _unresolvedProps4 + : resolveDefaultProps(_Component3, _unresolvedProps4); - while (_child !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child.lanes, _child.childLanes) - ); - subtreeFlags |= _child.subtreeFlags; - subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code - // smell because it assumes the commit phase is never concurrent with - // the render phase. Will address during refactor to alternate model. + return mountIncompleteClassComponent( + current, + workInProgress, + _Component3, + _resolvedProps4, + renderLanes + ); + } - _child.return = completedWork; - _child = _child.sibling; - } + case SuspenseListComponent: { + return updateSuspenseListComponent(current, workInProgress, renderLanes); } - completedWork.subtreeFlags |= subtreeFlags; - } else { - // Bubble up the earliest expiration time. - if ((completedWork.mode & ProfileMode) !== NoMode) { - // In profiling mode, resetChildExpirationTime is also used to reset - // profiler durations. - var _treeBaseDuration = completedWork.selfBaseDuration; - var _child2 = completedWork.child; + case ScopeComponent: { + break; + } - while (_child2 !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child2.lanes, _child2.childLanes) - ); // "Static" flags share the lifetime of the fiber/hook they belong to, - // so we should bubble those up even during a bailout. All the other - // flags have a lifetime only of a single render + commit, so we should - // ignore them. + case OffscreenComponent: { + return updateOffscreenComponent(current, workInProgress, renderLanes); + } - subtreeFlags |= _child2.subtreeFlags & StaticMask; - subtreeFlags |= _child2.flags & StaticMask; - _treeBaseDuration += _child2.treeBaseDuration; - _child2 = _child2.sibling; - } + case LegacyHiddenComponent: { + return updateLegacyHiddenComponent(current, workInProgress, renderLanes); + } + } - completedWork.treeBaseDuration = _treeBaseDuration; - } else { - var _child3 = completedWork.child; + { + throw Error( + "Unknown unit of work tag (" + + workInProgress.tag + + "). This error is likely caused by a bug in React. Please file an issue." + ); + } +} - while (_child3 !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child3.lanes, _child3.childLanes) - ); // "Static" flags share the lifetime of the fiber/hook they belong to, - // so we should bubble those up even during a bailout. All the other - // flags have a lifetime only of a single render + commit, so we should - // ignore them. +function markUpdate(workInProgress) { + // Tag the fiber with an update effect. This turns a Placement into + // a PlacementAndUpdate. + workInProgress.flags |= Update; +} - subtreeFlags |= _child3.subtreeFlags & StaticMask; - subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code - // smell because it assumes the commit phase is never concurrent with - // the render phase. Will address during refactor to alternate model. +function markRef$1(workInProgress) { + workInProgress.flags |= Ref; +} - _child3.return = completedWork; - _child3 = _child3.sibling; - } - } +function hadNoMutationsEffects(current, completedWork) { + var didBailout = current !== null && current.child === completedWork.child; - completedWork.subtreeFlags |= subtreeFlags; + if (didBailout) { + return true; } - completedWork.childLanes = newChildLanes; - return didBailout; -} + if ((completedWork.flags & ChildDeletion) !== NoFlags) { + return false; + } // TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties` + // then we only have to check the `completedWork.subtreeFlags`. -function completeWork(current, workInProgress, renderLanes) { - var newProps = workInProgress.pendingProps; + var child = completedWork.child; - switch (workInProgress.tag) { - case IndeterminateComponent: - case LazyComponent: - case SimpleMemoComponent: - case FunctionComponent: - case ForwardRef: - case Fragment: - case Mode: - case Profiler: - case ContextConsumer: - case MemoComponent: - bubbleProperties(workInProgress); - return null; + while (child !== null) { + if ( + (child.flags & MutationMask) !== NoFlags || + (child.subtreeFlags & MutationMask) !== NoFlags + ) { + return false; + } - case ClassComponent: { - var Component = workInProgress.type; + child = child.sibling; + } - if (isContextProvider(Component)) { - popContext(workInProgress); - } + return true; +} - bubbleProperties(workInProgress); - return null; - } +var appendAllChildren; +var updateHostContainer; +var updateHostComponent$1; +var updateHostText$1; - case HostRoot: { - var fiberRoot = workInProgress.stateNode; +{ + // Persistent host tree mode + appendAllChildren = function( + parent, + workInProgress, + needsVisibilityToggle, + isHidden + ) { + // We only have the top Fiber that was created but we need recurse down its + // children to find all the terminal nodes. + var node = workInProgress.child; - popHostContainer(workInProgress); - popTopLevelContextObject(workInProgress); - resetWorkInProgressVersions(); + while (node !== null) { + // eslint-disable-next-line no-labels + if (node.tag === HostComponent) { + var instance = node.stateNode; - if (fiberRoot.pendingContext) { - fiberRoot.context = fiberRoot.pendingContext; - fiberRoot.pendingContext = null; - } + if (needsVisibilityToggle && isHidden) { + // This child is inside a timed out tree. Hide it. + var props = node.memoizedProps; + var type = node.type; + instance = cloneHiddenInstance(instance); + } - if (current === null || current.child === null) { - // If we hydrated, pop so that we can delete any remaining children - // that weren't hydrated. - var wasHydrated = popHydrationState(); + appendInitialChild(parent, instance); + } else if (node.tag === HostText) { + var _instance = node.stateNode; - if (wasHydrated) { - // If we hydrated, then we'll need to schedule an update for - // the commit side-effects on the root. - markUpdate(workInProgress); - } else if (!fiberRoot.hydrate) { - // Schedule an effect to clear this container at the start of the next commit. - // This handles the case of React rendering into a container with previous children. - // It's also safe to do for updates too, because current.child would only be null - // if the previous render was null (so the the container would already be empty). - workInProgress.flags |= Snapshot; + if (needsVisibilityToggle && isHidden) { + // This child is inside a timed out tree. Hide it. + var text = node.memoizedProps; + _instance = cloneHiddenTextInstance(); } - } - updateHostContainer(current, workInProgress); - bubbleProperties(workInProgress); - return null; - } + appendInitialChild(parent, _instance); + } else if (node.tag === HostPortal); + else if (node.tag === SuspenseComponent) { + if ((node.flags & Update) !== NoFlags) { + // Need to toggle the visibility of the primary children. + var newIsHidden = node.memoizedState !== null; - case HostComponent: { - popHostContext(workInProgress); - var rootContainerInstance = getRootHostContainer(); - var type = workInProgress.type; + if (newIsHidden) { + var primaryChildParent = node.child; - if (current !== null && workInProgress.stateNode != null) { - updateHostComponent$1( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ); + if (primaryChildParent !== null) { + if (primaryChildParent.child !== null) { + primaryChildParent.child.return = primaryChildParent; + appendAllChildren( + parent, + primaryChildParent, + true, + newIsHidden + ); + } - if (current.ref !== workInProgress.ref) { - markRef$1(workInProgress); + var fallbackChildParent = primaryChildParent.sibling; + + if (fallbackChildParent !== null) { + fallbackChildParent.return = node; + node = fallbackChildParent; + continue; + } + } + } } - } else { - if (!newProps) { - if (!(workInProgress.stateNode !== null)) { - throw Error( - "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." - ); - } // This can happen when we abort work. - bubbleProperties(workInProgress); - return null; + if (node.child !== null) { + // Continue traversing like normal + node.child.return = node; + node = node.child; + continue; } + } else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; + } // $FlowFixMe This is correct but Flow is confused by the labeled break. - var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context - // "stack" as the parent. Then append children as we go in beginWork - // or completeWork depending on whether we want to add them top->down or - // bottom->up. Top->down is faster in IE11. + node = node; - var _wasHydrated = popHydrationState(); + if (node === workInProgress) { + return; + } - if (_wasHydrated) { - // TODO: Move this and createInstance step into the beginPhase - // to consolidate. - if (prepareToHydrateHostInstance()) { - // If changes to the hydrated node need to be applied at the - // commit-phase we mark this as such. - markUpdate(workInProgress); - } - } else { - var instance = createInstance( - type, - newProps, - rootContainerInstance, - currentHostContext, - workInProgress - ); - appendAllChildren(instance, workInProgress, false, false); - workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount. + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; } - if (workInProgress.ref !== null) { - // If there is a ref on a host node we need to schedule a callback - markRef$1(workInProgress); - } + node = node.return; } - bubbleProperties(workInProgress); - return null; + node.sibling.return = node.return; + node = node.sibling; } + }; // An unfortunate fork of appendAllChildren because we have two different parent types. - case HostText: { - var newText = newProps; + var appendAllChildrenToContainer = function( + containerChildSet, + workInProgress, + needsVisibilityToggle, + isHidden + ) { + // We only have the top Fiber that was created but we need recurse down its + // children to find all the terminal nodes. + var node = workInProgress.child; - if (current && workInProgress.stateNode != null) { - var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need - // to schedule a side-effect to do the updates. + while (node !== null) { + // eslint-disable-next-line no-labels + if (node.tag === HostComponent) { + var instance = node.stateNode; - updateHostText$1(current, workInProgress, oldText, newText); - } else { - if (typeof newText !== "string") { - if (!(workInProgress.stateNode !== null)) { - throw Error( - "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." - ); - } // This can happen when we abort work. + if (needsVisibilityToggle && isHidden) { + // This child is inside a timed out tree. Hide it. + var props = node.memoizedProps; + var type = node.type; + instance = cloneHiddenInstance(instance); + } + + appendChildToContainerChildSet(containerChildSet, instance); + } else if (node.tag === HostText) { + var _instance2 = node.stateNode; + + if (needsVisibilityToggle && isHidden) { + // This child is inside a timed out tree. Hide it. + var text = node.memoizedProps; + _instance2 = cloneHiddenTextInstance(); } - var _rootContainerInstance = getRootHostContainer(); + appendChildToContainerChildSet(containerChildSet, _instance2); + } else if (node.tag === HostPortal); + else if (node.tag === SuspenseComponent) { + if ((node.flags & Update) !== NoFlags) { + // Need to toggle the visibility of the primary children. + var newIsHidden = node.memoizedState !== null; - var _currentHostContext = getHostContext(); + if (newIsHidden) { + var primaryChildParent = node.child; - var _wasHydrated2 = popHydrationState(); + if (primaryChildParent !== null) { + if (primaryChildParent.child !== null) { + primaryChildParent.child.return = primaryChildParent; + appendAllChildrenToContainer( + containerChildSet, + primaryChildParent, + true, + newIsHidden + ); + } - if (_wasHydrated2) { - if (prepareToHydrateHostTextInstance()) { - markUpdate(workInProgress); + var fallbackChildParent = primaryChildParent.sibling; + + if (fallbackChildParent !== null) { + fallbackChildParent.return = node; + node = fallbackChildParent; + continue; + } + } } - } else { - workInProgress.stateNode = createTextInstance( - newText, - _rootContainerInstance, - _currentHostContext, - workInProgress - ); } - } - bubbleProperties(workInProgress); - return null; - } + if (node.child !== null) { + // Continue traversing like normal + node.child.return = node; + node = node.child; + continue; + } + } else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; + } // $FlowFixMe This is correct but Flow is confused by the labeled break. - case SuspenseComponent: { - popSuspenseContext(workInProgress); - var nextState = workInProgress.memoizedState; + node = node; - if ((workInProgress.flags & DidCapture) !== NoFlags) { - // Something suspended. Re-render with the fallback children. - workInProgress.lanes = renderLanes; // Do not reset the effect list. + if (node === workInProgress) { + return; + } - if ((workInProgress.mode & ProfileMode) !== NoMode) { - transferActualDuration(workInProgress); - } // Don't bubble properties in this case. + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; + } - return workInProgress; + node = node.return; } - var nextDidTimeout = nextState !== null; - var prevDidTimeout = false; + node.sibling.return = node.return; + node = node.sibling; + } + }; - if (current === null) { - if (workInProgress.memoizedProps.fallback !== undefined); - } else { - var prevState = current.memoizedState; - prevDidTimeout = prevState !== null; - } + updateHostContainer = function(current, workInProgress) { + var portalOrRoot = workInProgress.stateNode; + var childrenUnchanged = hadNoMutationsEffects(current, workInProgress); - if (nextDidTimeout && !prevDidTimeout) { - // TODO: This will still suspend a synchronous tree if anything - // in the concurrent tree already suspended during this render. - // This is a known bug. - if ((workInProgress.mode & ConcurrentMode) !== NoMode) { - // TODO: Move this back to throwException because this is too late - // if this is a large tree which is common for initial loads. We - // don't know if we should restart a render or not until we get - // this marker, and this is too late. - // If this render already had a ping or lower pri updates, - // and this is the first time we know we're going to suspend we - // should be able to immediately restart from within throwException. - var hasInvisibleChildContext = - current === null && - workInProgress.memoizedProps.unstable_avoidThisFallback !== true; + if (childrenUnchanged); + else { + var container = portalOrRoot.containerInfo; + var newChildSet = createContainerChildSet(container); // If children might have changed, we have to add them all to the set. - if ( - hasInvisibleChildContext || - hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext - ) - ) { - // If this was in an invisible tree or a new render, then showing - // this boundary is ok. - renderDidSuspend(); - } else { - // Otherwise, we're going to have to hide content so we should - // suspend for longer if possible. - renderDidSuspendDelayIfPossible(); - } - } - } + appendAllChildrenToContainer(newChildSet, workInProgress, false, false); + portalOrRoot.pendingChildren = newChildSet; // Schedule an update on the container to swap out the container. - { - // TODO: Only schedule updates if not prevDidTimeout. - if (nextDidTimeout) { - // If this boundary just timed out, schedule an effect to attach a - // retry listener to the promise. This flag is also used to hide the - // primary children. - workInProgress.flags |= Update; - } - } + markUpdate(workInProgress); + finalizeContainerChildren(container, newChildSet); + } + }; - bubbleProperties(workInProgress); + updateHostComponent$1 = function( + current, + workInProgress, + type, + newProps, + rootContainerInstance + ) { + var currentInstance = current.stateNode; + var oldProps = current.memoizedProps; // If there are no effects associated with this node, then none of our children had any updates. + // This guarantees that we can reuse all of them. - { - if ((workInProgress.mode & ProfileMode) !== NoMode) { - if (nextDidTimeout) { - // Don't count time spent in a timed out Suspense subtree as part of the base duration. - var _primaryChildFragment2 = workInProgress.child; + var childrenUnchanged = hadNoMutationsEffects(current, workInProgress); - if (_primaryChildFragment2 !== null) { - // $FlowFixMe Flow doesn't support type casting in combination with the -= operator - workInProgress.treeBaseDuration -= - _primaryChildFragment2.treeBaseDuration; - } - } - } - } + if (childrenUnchanged && oldProps === newProps) { + // No changes, just reuse the existing instance. + // Note that this might release a previous clone. + workInProgress.stateNode = currentInstance; + return; + } - return null; + var recyclableInstance = workInProgress.stateNode; + var currentHostContext = getHostContext(); + var updatePayload = null; + + if (oldProps !== newProps) { + updatePayload = prepareUpdate( + recyclableInstance, + type, + oldProps, + newProps + ); } - case HostPortal: - popHostContainer(workInProgress); - updateHostContainer(current, workInProgress); + if (childrenUnchanged && updatePayload === null) { + // No changes, just reuse the existing instance. + // Note that this might release a previous clone. + workInProgress.stateNode = currentInstance; + return; + } - if (current === null) { - preparePortalMount(workInProgress.stateNode.containerInfo); - } + var newInstance = cloneInstance( + currentInstance, + updatePayload, + type, + oldProps, + newProps, + workInProgress, + childrenUnchanged + ); - bubbleProperties(workInProgress); - return null; + workInProgress.stateNode = newInstance; - case ContextProvider: - // Pop provider fiber - var context = workInProgress.type._context; - popProvider(context, workInProgress); - bubbleProperties(workInProgress); - return null; + if (childrenUnchanged) { + // If there are no other effects in this tree, we need to flag this node as having one. + // Even though we're not going to use it for anything. + // Otherwise parents won't know that there are new children to propagate upwards. + markUpdate(workInProgress); + } else { + // If children might have changed, we have to add them all to the set. + appendAllChildren(newInstance, workInProgress, false, false); + } + }; - case IncompleteClassComponent: { - // Same as class component case. I put it down here so that the tags are - // sequential to ensure this switch is compiled to a jump table. - var _Component = workInProgress.type; + updateHostText$1 = function(current, workInProgress, oldText, newText) { + if (oldText !== newText) { + // If the text content differs, we'll create a new text instance for it. + var rootContainerInstance = getRootHostContainer(); + var currentHostContext = getHostContext(); + workInProgress.stateNode = createTextInstance( + newText, + rootContainerInstance, + currentHostContext, + workInProgress + ); // We'll have to mark it as having an effect, even though we won't use the effect for anything. + // This lets the parents know that at least one of their children has changed. - if (isContextProvider(_Component)) { - popContext(workInProgress); - } + markUpdate(workInProgress); + } else { + workInProgress.stateNode = current.stateNode; + } + }; +} + +function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { + switch (renderState.tailMode) { + case "hidden": { + // Any insertions at the end of the tail list after this point + // should be invisible. If there are already mounted boundaries + // anything before them are not considered for collapsing. + // Therefore we need to go through the whole tail to find if + // there are any. + var tailNode = renderState.tail; + var lastTailNode = null; - bubbleProperties(workInProgress); - return null; - } + while (tailNode !== null) { + if (tailNode.alternate !== null) { + lastTailNode = tailNode; + } - case SuspenseListComponent: { - popSuspenseContext(workInProgress); - var renderState = workInProgress.memoizedState; + tailNode = tailNode.sibling; + } // Next we're simply going to delete all insertions after the + // last rendered item. - if (renderState === null) { - // We're running in the default, "independent" mode. - // We don't do anything in this mode. - bubbleProperties(workInProgress); - return null; + if (lastTailNode === null) { + // All remaining items in the tail are insertions. + renderState.tail = null; + } else { + // Detach the insertion after the last node that was already + // inserted. + lastTailNode.sibling = null; } - var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; - var renderedTail = renderState.rendering; - - if (renderedTail === null) { - // We just rendered the head. - if (!didSuspendAlready) { - // This is the first pass. We need to figure out if anything is still - // suspended in the rendered set. - // If new content unsuspended, but there's still some content that - // didn't. Then we need to do a second pass that forces everything - // to keep showing their fallbacks. - // We might be suspended if something in this render pass suspended, or - // something in the previous committed pass suspended. Otherwise, - // there's no chance so we can skip the expensive call to - // findFirstSuspended. - var cannotBeSuspended = - renderHasNotSuspendedYet() && - (current === null || (current.flags & DidCapture) === NoFlags); - - if (!cannotBeSuspended) { - var row = workInProgress.child; + break; + } - while (row !== null) { - var suspended = findFirstSuspended(row); + case "collapsed": { + // Any insertions at the end of the tail list after this point + // should be invisible. If there are already mounted boundaries + // anything before them are not considered for collapsing. + // Therefore we need to go through the whole tail to find if + // there are any. + var _tailNode = renderState.tail; + var _lastTailNode = null; - if (suspended !== null) { - didSuspendAlready = true; - workInProgress.flags |= DidCapture; - cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as - // part of the second pass. In that case nothing will subscribe to - // its thennables. Instead, we'll transfer its thennables to the - // SuspenseList so that it can retry if they resolve. - // There might be multiple of these in the list but since we're - // going to wait for all of them anyway, it doesn't really matter - // which ones gets to ping. In theory we could get clever and keep - // track of how many dependencies remain but it gets tricky because - // in the meantime, we can add/remove/change items and dependencies. - // We might bail out of the loop before finding any but that - // doesn't matter since that means that the other boundaries that - // we did find already has their listeners attached. + while (_tailNode !== null) { + if (_tailNode.alternate !== null) { + _lastTailNode = _tailNode; + } - var newThennables = suspended.updateQueue; + _tailNode = _tailNode.sibling; + } // Next we're simply going to delete all insertions after the + // last rendered item. - if (newThennables !== null) { - workInProgress.updateQueue = newThennables; - workInProgress.flags |= Update; - } // Rerender the whole list, but this time, we'll force fallbacks - // to stay in place. - // Reset the effect flags before doing the second pass since that's now invalid. - // Reset the child fibers to their original state. + if (_lastTailNode === null) { + // All remaining items in the tail are insertions. + if (!hasRenderedATailFallback && renderState.tail !== null) { + // We suspended during the head. We want to show at least one + // row at the tail. So we'll keep on and cut off the rest. + renderState.tail.sibling = null; + } else { + renderState.tail = null; + } + } else { + // Detach the insertion after the last node that was already + // inserted. + _lastTailNode.sibling = null; + } - workInProgress.subtreeFlags = NoFlags; - resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately - // rerender the children. + break; + } + } +} - pushSuspenseContext( - workInProgress, - setShallowSuspenseContext( - suspenseStackCursor.current, - ForceSuspenseFallback - ) - ); // Don't bubble properties in this case. +function bubbleProperties(completedWork) { + var didBailout = + completedWork.alternate !== null && + completedWork.alternate.child === completedWork.child; + var newChildLanes = NoLanes; + var subtreeFlags = NoFlags; - return workInProgress.child; - } + if (!didBailout) { + // Bubble up the earliest expiration time. + if ((completedWork.mode & ProfileMode) !== NoMode) { + // In profiling mode, resetChildExpirationTime is also used to reset + // profiler durations. + var actualDuration = completedWork.actualDuration; + var treeBaseDuration = completedWork.selfBaseDuration; + var child = completedWork.child; - row = row.sibling; - } - } + while (child !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(child.lanes, child.childLanes) + ); + subtreeFlags |= child.subtreeFlags; + subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will + // only be updated if work is done on the fiber (i.e. it doesn't bailout). + // When work is done, it should bubble to the parent's actualDuration. If + // the fiber has not been cloned though, (meaning no work was done), then + // this value will reflect the amount of time spent working on a previous + // render. In that case it should not bubble. We determine whether it was + // cloned by comparing the child pointer. - if (renderState.tail !== null && now() > getRenderTargetTime()) { - // We have already passed our CPU deadline but we still have rows - // left in the tail. We'll just give up further attempts to render - // the main content and only render fallbacks. - workInProgress.flags |= DidCapture; - didSuspendAlready = true; - cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this - // to get it started back up to attempt the next item. While in terms - // of priority this work has the same priority as this current render, - // it's not part of the same transition once the transition has - // committed. If it's sync, we still want to yield so that it can be - // painted. Conceptually, this is really the same as pinging. - // We can use any RetryLane even if it's the one currently rendering - // since we're leaving it behind on this node. + actualDuration += child.actualDuration; + treeBaseDuration += child.treeBaseDuration; + child = child.sibling; + } - workInProgress.lanes = SomeRetryLane; - } - } else { - cutOffTailIfNeeded(renderState, false); - } // Next we're going to render the tail. - } else { - // Append the rendered row to the child list. - if (!didSuspendAlready) { - var _suspended = findFirstSuspended(renderedTail); + completedWork.actualDuration = actualDuration; + completedWork.treeBaseDuration = treeBaseDuration; + } else { + var _child = completedWork.child; - if (_suspended !== null) { - workInProgress.flags |= DidCapture; - didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't - // get lost if this row ends up dropped during a second pass. + while (_child !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child.lanes, _child.childLanes) + ); + subtreeFlags |= _child.subtreeFlags; + subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code + // smell because it assumes the commit phase is never concurrent with + // the render phase. Will address during refactor to alternate model. - var _newThennables = _suspended.updateQueue; + _child.return = completedWork; + _child = _child.sibling; + } + } - if (_newThennables !== null) { - workInProgress.updateQueue = _newThennables; - workInProgress.flags |= Update; - } + completedWork.subtreeFlags |= subtreeFlags; + } else { + // Bubble up the earliest expiration time. + if ((completedWork.mode & ProfileMode) !== NoMode) { + // In profiling mode, resetChildExpirationTime is also used to reset + // profiler durations. + var _treeBaseDuration = completedWork.selfBaseDuration; + var _child2 = completedWork.child; - cutOffTailIfNeeded(renderState, true); // This might have been modified. + while (_child2 !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child2.lanes, _child2.childLanes) + ); // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. - if ( - renderState.tail === null && - renderState.tailMode === "hidden" && - !renderedTail.alternate && - !getIsHydrating() // We don't cut it if we're hydrating. - ) { - // We're done. - bubbleProperties(workInProgress); - return null; - } - } else if ( - // The time it took to render last row is greater than the remaining - // time we have to render. So rendering one more row would likely - // exceed it. - now() * 2 - renderState.renderingStartTime > - getRenderTargetTime() && - renderLanes !== OffscreenLane - ) { - // We have now passed our CPU deadline and we'll just give up further - // attempts to render the main content and only render fallbacks. - // The assumption is that this is usually faster. - workInProgress.flags |= DidCapture; - didSuspendAlready = true; - cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this - // to get it started back up to attempt the next item. While in terms - // of priority this work has the same priority as this current render, - // it's not part of the same transition once the transition has - // committed. If it's sync, we still want to yield so that it can be - // painted. Conceptually, this is really the same as pinging. - // We can use any RetryLane even if it's the one currently rendering - // since we're leaving it behind on this node. + subtreeFlags |= _child2.subtreeFlags & StaticMask; + subtreeFlags |= _child2.flags & StaticMask; + _treeBaseDuration += _child2.treeBaseDuration; + _child2 = _child2.sibling; + } - workInProgress.lanes = SomeRetryLane; - } - } + completedWork.treeBaseDuration = _treeBaseDuration; + } else { + var _child3 = completedWork.child; - if (renderState.isBackwards) { - // The effect list of the backwards tail will have been added - // to the end. This breaks the guarantee that life-cycles fire in - // sibling order but that isn't a strong guarantee promised by React. - // Especially since these might also just pop in during future commits. - // Append to the beginning of the list. - renderedTail.sibling = workInProgress.child; - workInProgress.child = renderedTail; - } else { - var previousSibling = renderState.last; + while (_child3 !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child3.lanes, _child3.childLanes) + ); // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. - if (previousSibling !== null) { - previousSibling.sibling = renderedTail; - } else { - workInProgress.child = renderedTail; - } + subtreeFlags |= _child3.subtreeFlags & StaticMask; + subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code + // smell because it assumes the commit phase is never concurrent with + // the render phase. Will address during refactor to alternate model. - renderState.last = renderedTail; - } + _child3.return = completedWork; + _child3 = _child3.sibling; } + } - if (renderState.tail !== null) { - // We still have tail rows to render. - // Pop a row. - var next = renderState.tail; - renderState.rendering = next; - renderState.tail = next.sibling; - renderState.renderingStartTime = now(); - next.sibling = null; // Restore the context. - // TODO: We can probably just avoid popping it instead and only - // setting it the first time we go from not suspended to suspended. + completedWork.subtreeFlags |= subtreeFlags; + } - var suspenseContext = suspenseStackCursor.current; + completedWork.childLanes = newChildLanes; + return didBailout; +} - if (didSuspendAlready) { - suspenseContext = setShallowSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); - } else { - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - } +function completeWork(current, workInProgress, renderLanes) { + var newProps = workInProgress.pendingProps; - pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. - // Don't bubble properties in this case. + switch (workInProgress.tag) { + case IndeterminateComponent: + case LazyComponent: + case SimpleMemoComponent: + case FunctionComponent: + case ForwardRef: + case Fragment: + case Mode: + case Profiler: + case ContextConsumer: + case MemoComponent: + bubbleProperties(workInProgress); + return null; - return next; + case ClassComponent: { + var Component = workInProgress.type; + + if (isContextProvider(Component)) { + popContext(workInProgress); } bubbleProperties(workInProgress); return null; } - case ScopeComponent: { - break; - } + case HostRoot: { + var fiberRoot = workInProgress.stateNode; - case OffscreenComponent: - case LegacyHiddenComponent: { - popRenderLanes(workInProgress); - var _nextState = workInProgress.memoizedState; - var nextIsHidden = _nextState !== null; + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + resetWorkInProgressVersions(); - if (current !== null) { - var _prevState = current.memoizedState; - var prevIsHidden = _prevState !== null; + if (fiberRoot.pendingContext) { + fiberRoot.context = fiberRoot.pendingContext; + fiberRoot.pendingContext = null; + } - if ( - prevIsHidden !== nextIsHidden && - newProps.mode !== "unstable-defer-without-hiding" - ) { - workInProgress.flags |= Update; - } - } // Don't bubble properties for hidden children. + if (current === null || current.child === null) { + // If we hydrated, pop so that we can delete any remaining children + // that weren't hydrated. + var wasHydrated = popHydrationState(); - if ( - !nextIsHidden || - includesSomeLane(subtreeRenderLanes, OffscreenLane) || - (workInProgress.mode & ConcurrentMode) === NoMode - ) { - bubbleProperties(workInProgress); + if (wasHydrated) { + // If we hydrated, then we'll need to schedule an update for + // the commit side-effects on the root. + markUpdate(workInProgress); + } else if (!fiberRoot.hydrate) { + // Schedule an effect to clear this container at the start of the next commit. + // This handles the case of React rendering into a container with previous children. + // It's also safe to do for updates too, because current.child would only be null + // if the previous render was null (so the the container would already be empty). + workInProgress.flags |= Snapshot; + } } + updateHostContainer(current, workInProgress); + bubbleProperties(workInProgress); return null; } - } - { - throw Error( - "Unknown unit of work tag (" + - workInProgress.tag + - "). This error is likely caused by a bug in React. Please file an issue." - ); - } -} + case HostComponent: { + popHostContext(workInProgress); + var rootContainerInstance = getRootHostContainer(); + var type = workInProgress.type; -function unwindWork(workInProgress, renderLanes) { - switch (workInProgress.tag) { - case ClassComponent: { - var Component = workInProgress.type; + if (current !== null && workInProgress.stateNode != null) { + updateHostComponent$1( + current, + workInProgress, + type, + newProps, + rootContainerInstance + ); - if (isContextProvider(Component)) { - popContext(workInProgress); - } + if (current.ref !== workInProgress.ref) { + markRef$1(workInProgress); + } + } else { + if (!newProps) { + if (!(workInProgress.stateNode !== null)) { + throw Error( + "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." + ); + } // This can happen when we abort work. - var flags = workInProgress.flags; + bubbleProperties(workInProgress); + return null; + } - if (flags & ShouldCapture) { - workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; + var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context + // "stack" as the parent. Then append children as we go in beginWork + // or completeWork depending on whether we want to add them top->down or + // bottom->up. Top->down is faster in IE11. - if ((workInProgress.mode & ProfileMode) !== NoMode) { - transferActualDuration(workInProgress); + var _wasHydrated = popHydrationState(); + + if (_wasHydrated) { + // TODO: Move this and createInstance step into the beginPhase + // to consolidate. + if (prepareToHydrateHostInstance()) { + // If changes to the hydrated node need to be applied at the + // commit-phase we mark this as such. + markUpdate(workInProgress); + } + } else { + var instance = createInstance( + type, + newProps, + rootContainerInstance, + currentHostContext, + workInProgress + ); + appendAllChildren(instance, workInProgress, false, false); + workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount. } - return workInProgress; + if (workInProgress.ref !== null) { + // If there is a ref on a host node we need to schedule a callback + markRef$1(workInProgress); + } } + bubbleProperties(workInProgress); return null; } - case HostRoot: { - popHostContainer(workInProgress); - popTopLevelContextObject(workInProgress); - resetWorkInProgressVersions(); - var _flags = workInProgress.flags; + case HostText: { + var newText = newProps; - if (!((_flags & DidCapture) === NoFlags)) { - throw Error( - "The root failed to unmount after an error. This is likely a bug in React. Please file an issue." - ); + if (current && workInProgress.stateNode != null) { + var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need + // to schedule a side-effect to do the updates. + + updateHostText$1(current, workInProgress, oldText, newText); + } else { + if (typeof newText !== "string") { + if (!(workInProgress.stateNode !== null)) { + throw Error( + "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." + ); + } // This can happen when we abort work. + } + + var _rootContainerInstance = getRootHostContainer(); + + var _currentHostContext = getHostContext(); + + var _wasHydrated2 = popHydrationState(); + + if (_wasHydrated2) { + if (prepareToHydrateHostTextInstance()) { + markUpdate(workInProgress); + } + } else { + workInProgress.stateNode = createTextInstance( + newText, + _rootContainerInstance, + _currentHostContext, + workInProgress + ); + } } - workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture; - return workInProgress; - } - - case HostComponent: { - // TODO: popHydrationState - popHostContext(workInProgress); + bubbleProperties(workInProgress); return null; } case SuspenseComponent: { popSuspenseContext(workInProgress); + var nextState = workInProgress.memoizedState; - var _flags2 = workInProgress.flags; - - if (_flags2 & ShouldCapture) { - workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary. + if ((workInProgress.flags & DidCapture) !== NoFlags) { + // Something suspended. Re-render with the fallback children. + workInProgress.lanes = renderLanes; // Do not reset the effect list. if ((workInProgress.mode & ProfileMode) !== NoMode) { transferActualDuration(workInProgress); - } + } // Don't bubble properties in this case. return workInProgress; } - return null; - } + var nextDidTimeout = nextState !== null; + var prevDidTimeout = false; - case SuspenseListComponent: { - popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been - // caught by a nested boundary. If not, it should bubble through. + if (current === null) { + if (workInProgress.memoizedProps.fallback !== undefined); + } else { + var prevState = current.memoizedState; + prevDidTimeout = prevState !== null; + } + + if (nextDidTimeout && !prevDidTimeout) { + // TODO: This will still suspend a synchronous tree if anything + // in the concurrent tree already suspended during this render. + // This is a known bug. + if ((workInProgress.mode & ConcurrentMode) !== NoMode) { + // TODO: Move this back to throwException because this is too late + // if this is a large tree which is common for initial loads. We + // don't know if we should restart a render or not until we get + // this marker, and this is too late. + // If this render already had a ping or lower pri updates, + // and this is the first time we know we're going to suspend we + // should be able to immediately restart from within throwException. + var hasInvisibleChildContext = + current === null && + workInProgress.memoizedProps.unstable_avoidThisFallback !== true; + + if ( + hasInvisibleChildContext || + hasSuspenseContext( + suspenseStackCursor.current, + InvisibleParentSuspenseContext + ) + ) { + // If this was in an invisible tree or a new render, then showing + // this boundary is ok. + renderDidSuspend(); + } else { + // Otherwise, we're going to have to hide content so we should + // suspend for longer if possible. + renderDidSuspendDelayIfPossible(); + } + } + } + + { + // TODO: Only schedule updates if not prevDidTimeout. + if (nextDidTimeout) { + // If this boundary just timed out, schedule an effect to attach a + // retry listener to the promise. This flag is also used to hide the + // primary children. + workInProgress.flags |= Update; + } + } + + bubbleProperties(workInProgress); + + { + if ((workInProgress.mode & ProfileMode) !== NoMode) { + if (nextDidTimeout) { + // Don't count time spent in a timed out Suspense subtree as part of the base duration. + var _primaryChildFragment2 = workInProgress.child; + + if (_primaryChildFragment2 !== null) { + // $FlowFixMe Flow doesn't support type casting in combination with the -= operator + workInProgress.treeBaseDuration -= + _primaryChildFragment2.treeBaseDuration; + } + } + } + } return null; } case HostPortal: popHostContainer(workInProgress); + updateHostContainer(current, workInProgress); + + if (current === null) { + preparePortalMount(workInProgress.stateNode.containerInfo); + } + + bubbleProperties(workInProgress); return null; case ContextProvider: + // Pop provider fiber var context = workInProgress.type._context; popProvider(context, workInProgress); + bubbleProperties(workInProgress); return null; - case OffscreenComponent: - case LegacyHiddenComponent: - popRenderLanes(workInProgress); - - return null; - - case CacheComponent: - return null; - - default: - return null; - } -} - -function unwindInterruptedWork(interruptedWork, renderLanes) { - switch (interruptedWork.tag) { - case ClassComponent: { - var childContextTypes = interruptedWork.type.childContextTypes; + case IncompleteClassComponent: { + // Same as class component case. I put it down here so that the tags are + // sequential to ensure this switch is compiled to a jump table. + var _Component = workInProgress.type; - if (childContextTypes !== null && childContextTypes !== undefined) { - popContext(interruptedWork); + if (isContextProvider(_Component)) { + popContext(workInProgress); } - break; - } - - case HostRoot: { - popHostContainer(interruptedWork); - popTopLevelContextObject(interruptedWork); - resetWorkInProgressVersions(); - break; - } - - case HostComponent: { - popHostContext(interruptedWork); - break; + bubbleProperties(workInProgress); + return null; } - case HostPortal: - popHostContainer(interruptedWork); - break; - - case SuspenseComponent: - popSuspenseContext(interruptedWork); - break; - - case SuspenseListComponent: - popSuspenseContext(interruptedWork); - break; + case SuspenseListComponent: { + popSuspenseContext(workInProgress); + var renderState = workInProgress.memoizedState; - case ContextProvider: - var context = interruptedWork.type._context; - popProvider(context, interruptedWork); - break; + if (renderState === null) { + // We're running in the default, "independent" mode. + // We don't do anything in this mode. + bubbleProperties(workInProgress); + return null; + } - case OffscreenComponent: - case LegacyHiddenComponent: - popRenderLanes(interruptedWork); + var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; + var renderedTail = renderState.rendering; - break; - } -} + if (renderedTail === null) { + // We just rendered the head. + if (!didSuspendAlready) { + // This is the first pass. We need to figure out if anything is still + // suspended in the rendered set. + // If new content unsuspended, but there's still some content that + // didn't. Then we need to do a second pass that forces everything + // to keep showing their fallbacks. + // We might be suspended if something in this render pass suspended, or + // something in the previous committed pass suspended. Otherwise, + // there's no chance so we can skip the expensive call to + // findFirstSuspended. + var cannotBeSuspended = + renderHasNotSuspendedYet() && + (current === null || (current.flags & DidCapture) === NoFlags); -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: getStackByFiberInDevAndProd(source) - }; -} + if (!cannotBeSuspended) { + var row = workInProgress.child; -if ( - !( - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog === - "function" - ) -) { - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -} + while (row !== null) { + var suspended = findFirstSuspended(row); -function showErrorDialog(boundary, errorInfo) { - var capturedError = { - componentStack: errorInfo.stack !== null ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - boundary !== null && boundary.tag === ClassComponent - ? boundary.stateNode - : null - }; - return ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog( - capturedError - ); -} + if (suspended !== null) { + didSuspendAlready = true; + workInProgress.flags |= DidCapture; + cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as + // part of the second pass. In that case nothing will subscribe to + // its thennables. Instead, we'll transfer its thennables to the + // SuspenseList so that it can retry if they resolve. + // There might be multiple of these in the list but since we're + // going to wait for all of them anyway, it doesn't really matter + // which ones gets to ping. In theory we could get clever and keep + // track of how many dependencies remain but it gets tricky because + // in the meantime, we can add/remove/change items and dependencies. + // We might bail out of the loop before finding any but that + // doesn't matter since that means that the other boundaries that + // we did find already has their listeners attached. -function logCapturedError(boundary, errorInfo) { - try { - var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. - // This enables renderers like ReactNative to better manage redbox behavior. + var newThennables = suspended.updateQueue; - if (logError === false) { - return; - } + if (newThennables !== null) { + workInProgress.updateQueue = newThennables; + workInProgress.flags |= Update; + } // Rerender the whole list, but this time, we'll force fallbacks + // to stay in place. + // Reset the effect flags before doing the second pass since that's now invalid. + // Reset the child fibers to their original state. - var error = errorInfo.value; + workInProgress.subtreeFlags = NoFlags; + resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately + // rerender the children. - if (true) { - var source = errorInfo.source; - var stack = errorInfo.stack; - var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling - // `preventDefault()` in window `error` handler. - // We record this information as an expando on the error. + pushSuspenseContext( + workInProgress, + setShallowSuspenseContext( + suspenseStackCursor.current, + ForceSuspenseFallback + ) + ); // Don't bubble properties in this case. - if (error != null && error._suppressLogging) { - if (boundary.tag === ClassComponent) { - // The error is recoverable and was silenced. - // Ignore it and don't print the stack addendum. - // This is handy for testing error boundaries without noise. - return; - } // The error is fatal. Since the silencing might have - // been accidental, we'll surface it anyway. - // However, the browser would have silenced the original error - // so we'll print it first, and then print the stack addendum. + return workInProgress.child; + } - console["error"](error); // Don't transform to our wrapper - // For a more detailed description of this block, see: - // https://github.com/facebook/react/pull/13384 - } + row = row.sibling; + } + } - var componentName = source ? getComponentNameFromFiber(source) : null; - var componentNameMessage = componentName - ? "The above error occurred in the <" + componentName + "> component:" - : "The above error occurred in one of your React components:"; - var errorBoundaryMessage; + if (renderState.tail !== null && now() > getRenderTargetTime()) { + // We have already passed our CPU deadline but we still have rows + // left in the tail. We'll just give up further attempts to render + // the main content and only render fallbacks. + workInProgress.flags |= DidCapture; + didSuspendAlready = true; + cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this + // to get it started back up to attempt the next item. While in terms + // of priority this work has the same priority as this current render, + // it's not part of the same transition once the transition has + // committed. If it's sync, we still want to yield so that it can be + // painted. Conceptually, this is really the same as pinging. + // We can use any RetryLane even if it's the one currently rendering + // since we're leaving it behind on this node. - if (boundary.tag === HostRoot) { - errorBoundaryMessage = - "Consider adding an error boundary to your tree to customize error handling behavior.\n" + - "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries."; + workInProgress.lanes = SomeRetryLane; + } + } else { + cutOffTailIfNeeded(renderState, false); + } // Next we're going to render the tail. } else { - var errorBoundaryName = - getComponentNameFromFiber(boundary) || "Anonymous"; - errorBoundaryMessage = - "React will try to recreate this component tree from scratch " + - ("using the error boundary you provided, " + errorBoundaryName + "."); - } - - var combinedMessage = - componentNameMessage + - "\n" + - 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. + // Append the rendered row to the child list. + if (!didSuspendAlready) { + var _suspended = findFirstSuspended(renderedTail); - console["error"](combinedMessage); // Don't transform to our wrapper - } else { - // In production, we print the error directly. - // This will include the message, the JS stack, and anything the browser wants to show. - // We pass the error object instead of custom message so that the browser displays the error natively. - console["error"](error); // Don't transform to our wrapper - } - } catch (e) { - // This method must not throw, or React internal state will get messed up. - // If console.error is overridden, or logCapturedError() shows a dialog that throws, - // we want to report this error outside of the normal stack as a last resort. - // https://github.com/facebook/react/issues/13188 - setTimeout(function() { - throw e; - }); - } -} + if (_suspended !== null) { + workInProgress.flags |= DidCapture; + didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't + // get lost if this row ends up dropped during a second pass. -var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; + var _newThennables = _suspended.updateQueue; -function createRootErrorUpdate(fiber, errorInfo, lane) { - var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. + if (_newThennables !== null) { + workInProgress.updateQueue = _newThennables; + workInProgress.flags |= Update; + } - update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property - // being called "element". + cutOffTailIfNeeded(renderState, true); // This might have been modified. - update.payload = { - element: null - }; - var error = errorInfo.value; + if ( + renderState.tail === null && + renderState.tailMode === "hidden" && + !renderedTail.alternate && + !getIsHydrating() // We don't cut it if we're hydrating. + ) { + // We're done. + bubbleProperties(workInProgress); + return null; + } + } else if ( + // The time it took to render last row is greater than the remaining + // time we have to render. So rendering one more row would likely + // exceed it. + now() * 2 - renderState.renderingStartTime > + getRenderTargetTime() && + renderLanes !== OffscreenLane + ) { + // We have now passed our CPU deadline and we'll just give up further + // attempts to render the main content and only render fallbacks. + // The assumption is that this is usually faster. + workInProgress.flags |= DidCapture; + didSuspendAlready = true; + cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this + // to get it started back up to attempt the next item. While in terms + // of priority this work has the same priority as this current render, + // it's not part of the same transition once the transition has + // committed. If it's sync, we still want to yield so that it can be + // painted. Conceptually, this is really the same as pinging. + // We can use any RetryLane even if it's the one currently rendering + // since we're leaving it behind on this node. - update.callback = function() { - onUncaughtError(error); - logCapturedError(fiber, errorInfo); - }; + workInProgress.lanes = SomeRetryLane; + } + } - return update; -} + if (renderState.isBackwards) { + // The effect list of the backwards tail will have been added + // to the end. This breaks the guarantee that life-cycles fire in + // sibling order but that isn't a strong guarantee promised by React. + // Especially since these might also just pop in during future commits. + // Append to the beginning of the list. + renderedTail.sibling = workInProgress.child; + workInProgress.child = renderedTail; + } else { + var previousSibling = renderState.last; -function createClassErrorUpdate(fiber, errorInfo, lane) { - var update = createUpdate(NoTimestamp, lane); - update.tag = CaptureUpdate; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if (previousSibling !== null) { + previousSibling.sibling = renderedTail; + } else { + workInProgress.child = renderedTail; + } - if (typeof getDerivedStateFromError === "function") { - var error$1 = errorInfo.value; + renderState.last = renderedTail; + } + } - update.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error$1); - }; - } + if (renderState.tail !== null) { + // We still have tail rows to render. + // Pop a row. + var next = renderState.tail; + renderState.rendering = next; + renderState.tail = next.sibling; + renderState.renderingStartTime = now(); + next.sibling = null; // Restore the context. + // TODO: We can probably just avoid popping it instead and only + // setting it the first time we go from not suspended to suspended. - var inst = fiber.stateNode; + var suspenseContext = suspenseStackCursor.current; - if (inst !== null && typeof inst.componentDidCatch === "function") { - update.callback = function callback() { - { - markFailedErrorBoundaryForHotReloading(fiber); - } + if (didSuspendAlready) { + suspenseContext = setShallowSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + } else { + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + } - if (typeof getDerivedStateFromError !== "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 getDerivedStateFromError is - // not defined. - markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined + pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. + // Don't bubble properties in this case. - logCapturedError(fiber, errorInfo); + return next; } - var error$1 = errorInfo.value; - var stack = errorInfo.stack; - this.componentDidCatch(error$1, { - componentStack: stack !== null ? stack : "" - }); + bubbleProperties(workInProgress); + return null; + } - { - if (typeof getDerivedStateFromError !== "function") { - // If componentDidCatch is the only error boundary method defined, - // then it needs to call setState to recover from errors. - // If no state update is scheduled then the boundary will swallow the error. - if (!includesSomeLane(fiber.lanes, SyncLane)) { - error( - "%s: Error boundaries should implement getDerivedStateFromError(). " + - "In that method, return a state update to display an error message or fallback UI.", - getComponentNameFromFiber(fiber) || "Unknown" - ); - } - } - } - }; - } else { - update.callback = function() { - markFailedErrorBoundaryForHotReloading(fiber); - }; - } + case ScopeComponent: { + break; + } - return update; -} + case OffscreenComponent: + case LegacyHiddenComponent: { + popRenderLanes(workInProgress); + var _nextState = workInProgress.memoizedState; + var nextIsHidden = _nextState !== null; -function attachPingListener(root, wakeable, lanes) { - // Attach a listener to the promise to "ping" the root and retry. But only if - // one does not already exist for the lanes we're currently rendering (which - // acts like a "thread ID" here). - var pingCache = root.pingCache; - var threadIDs; + if (current !== null) { + var _prevState = current.memoizedState; + var prevIsHidden = _prevState !== null; - if (pingCache === null) { - pingCache = root.pingCache = new PossiblyWeakMap$1(); - threadIDs = new Set(); - pingCache.set(wakeable, threadIDs); - } else { - threadIDs = pingCache.get(wakeable); + if ( + prevIsHidden !== nextIsHidden && + newProps.mode !== "unstable-defer-without-hiding" + ) { + workInProgress.flags |= Update; + } + } // Don't bubble properties for hidden children. - if (threadIDs === undefined) { - threadIDs = new Set(); - pingCache.set(wakeable, threadIDs); + if ( + !nextIsHidden || + includesSomeLane(subtreeRenderLanes, OffscreenLane) || + (workInProgress.mode & ConcurrentMode) === NoMode + ) { + bubbleProperties(workInProgress); + } + + return null; } } - if (!threadIDs.has(lanes)) { - // Memoize using the thread ID to prevent redundant listeners. - threadIDs.add(lanes); - var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); - - wakeable.then(ping, ping); + { + throw Error( + "Unknown unit of work tag (" + + workInProgress.tag + + "). This error is likely caused by a bug in React. Please file an issue." + ); } } -function throwException( - root, - returnFiber, - sourceFiber, - value, - rootRenderLanes -) { - // The source fiber did not complete. - sourceFiber.flags |= Incomplete; +function unwindWork(workInProgress, renderLanes) { + switch (workInProgress.tag) { + case ClassComponent: { + var Component = workInProgress.type; - if ( - value !== null && - typeof value === "object" && - typeof value.then === "function" - ) { - var wakeable = value; - // A legacy mode Suspense quirk, only relevant to hook components. + if (isContextProvider(Component)) { + popContext(workInProgress); + } - var tag = sourceFiber.tag; + var flags = workInProgress.flags; - if ( - (sourceFiber.mode & ConcurrentMode) === NoMode && - (tag === FunctionComponent || - tag === ForwardRef || - tag === SimpleMemoComponent) - ) { - var currentSource = sourceFiber.alternate; + if (flags & ShouldCapture) { + workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; - if (currentSource) { - sourceFiber.updateQueue = currentSource.updateQueue; - sourceFiber.memoizedState = currentSource.memoizedState; - sourceFiber.lanes = currentSource.lanes; - } else { - sourceFiber.updateQueue = null; - sourceFiber.memoizedState = null; + if ((workInProgress.mode & ProfileMode) !== NoMode) { + transferActualDuration(workInProgress); + } + + return workInProgress; } + + return null; } - var hasInvisibleParentBoundary = hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext - ); // Schedule the nearest Suspense to re-render the timed out view. + case HostRoot: { + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + resetWorkInProgressVersions(); + var _flags = workInProgress.flags; - var _workInProgress = returnFiber; + if (!((_flags & DidCapture) === NoFlags)) { + throw Error( + "The root failed to unmount after an error. This is likely a bug in React. Please file an issue." + ); + } - do { - if ( - _workInProgress.tag === SuspenseComponent && - shouldCaptureSuspense(_workInProgress, hasInvisibleParentBoundary) - ) { - // Found the nearest boundary. - // Stash the promise on the boundary fiber. If the boundary times out, we'll - // attach another listener to flip the boundary back to its normal state. - var wakeables = _workInProgress.updateQueue; + workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture; + return workInProgress; + } - if (wakeables === null) { - var updateQueue = new Set(); - updateQueue.add(wakeable); - _workInProgress.updateQueue = updateQueue; - } else { - wakeables.add(wakeable); - } // If the boundary is in legacy mode, we should *not* - // suspend the commit. Pretend as if the suspended component rendered - // null and keep rendering. In the commit phase, we'll schedule a - // subsequent synchronous update to re-render the Suspense. - // - // Note: It doesn't matter whether the component that suspended was - // inside a concurrent mode tree. If the Suspense is outside of it, we - // should *not* suspend the commit. - // - // If the suspense boundary suspended itself suspended, we don't have to - // do this trick because nothing was partially started. We can just - // directly do a second pass over the fallback in this render and - // pretend we meant to render that directly. + case HostComponent: { + // TODO: popHydrationState + popHostContext(workInProgress); + return null; + } - if ( - (_workInProgress.mode & ConcurrentMode) === NoMode && - _workInProgress !== returnFiber - ) { - _workInProgress.flags |= DidCapture; - sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. - // But we shouldn't call any lifecycle methods or callbacks. Remove - // all lifecycle effect tags. + case SuspenseComponent: { + popSuspenseContext(workInProgress); - sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); + var _flags2 = workInProgress.flags; - if (sourceFiber.tag === ClassComponent) { - var _currentSourceFiber = sourceFiber.alternate; + if (_flags2 & ShouldCapture) { + workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary. - if (_currentSourceFiber === null) { - // This is a new mount. Change the tag so it's not mistaken for a - // completed class component. For example, we should not call - // componentWillUnmount if it is deleted. - sourceFiber.tag = IncompleteClassComponent; - } else { - // When we try rendering again, we should not reuse the current fiber, - // since it's known to be in an inconsistent state. Use a force update to - // prevent a bail out. - var update = createUpdate(NoTimestamp, SyncLane); - update.tag = ForceUpdate; - enqueueUpdate(sourceFiber, update); - } - } // The source fiber did not complete. Mark it with Sync priority to - // indicate that it still has pending work. + if ((workInProgress.mode & ProfileMode) !== NoMode) { + transferActualDuration(workInProgress); + } - sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); // Exit without suspending. + return workInProgress; + } - return; - } // Confirmed that the boundary is in a concurrent mode tree. Continue - // with the normal suspend path. - // - // After this we'll use a set of heuristics to determine whether this - // render pass will run to completion or restart or "suspend" the commit. - // The actual logic for this is spread out in different places. - // - // This first principle is that if we're going to suspend when we complete - // a root, then we should also restart if we get an update or ping that - // might unsuspend it, and vice versa. The only reason to suspend is - // because you think you might want to restart before committing. However, - // it doesn't make sense to restart only while in the period we're suspended. - // - // Restarting too aggressively is also not good because it starves out any - // intermediate loading state. So we use heuristics to determine when. - // Suspense Heuristics - // - // If nothing threw a Promise or all the same fallbacks are already showing, - // then don't suspend/restart. - // - // If this is an initial render of a new tree of Suspense boundaries and - // those trigger a fallback, then don't suspend/restart. We want to ensure - // that we can show the initial loading state as quickly as possible. - // - // If we hit a "Delayed" case, such as when we'd switch from content back into - // a fallback, then we should always suspend/restart. Transitions apply - // to this case. If none is defined, JND is used instead. - // - // If we're already showing a fallback and it gets "retried", allowing us to show - // another level, but there's still an inner boundary that would show a fallback, - // then we suspend/restart for 500ms since the last time we showed a fallback - // anywhere in the tree. This effectively throttles progressive loading into a - // consistent train of commits. This also gives us an opportunity to restart to - // get to the completed state slightly earlier. - // - // If there's ambiguity due to batching it's resolved in preference of: - // 1) "delayed", 2) "initial render", 3) "retry". - // - // We want to ensure that a "busy" state doesn't get force committed. We want to - // ensure that new initial loading states can commit as soon as possible. + return null; + } - attachPingListener(root, wakeable, rootRenderLanes); - _workInProgress.flags |= ShouldCapture; - _workInProgress.lanes = rootRenderLanes; - return; - } // This boundary already captured during this render. Continue to the next - // boundary. + case SuspenseListComponent: { + popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been + // caught by a nested boundary. If not, it should bubble through. - _workInProgress = _workInProgress.return; - } while (_workInProgress !== null); // No boundary was found. Fallthrough to error mode. - // TODO: Use invariant so the message is stripped in prod? + return null; + } + + case HostPortal: + popHostContainer(workInProgress); + return null; + + case ContextProvider: + var context = workInProgress.type._context; + popProvider(context, workInProgress); + return null; + + case OffscreenComponent: + case LegacyHiddenComponent: + popRenderLanes(workInProgress); - value = new Error( - (getComponentNameFromFiber(sourceFiber) || "A React component") + - " suspended while rendering, but no fallback UI was specified.\n" + - "\n" + - "Add a component higher in the tree to " + - "provide a loading indicator or placeholder to display." - ); - } // We didn't find a boundary that could handle this type of exception. Start - // over and traverse parent path again, this time treating the exception - // as an error. + return null; - renderDidError(); - value = createCapturedValue(value, sourceFiber); - var workInProgress = returnFiber; + case CacheComponent: + return null; - do { - switch (workInProgress.tag) { - case HostRoot: { - var _errorInfo = value; - workInProgress.flags |= ShouldCapture; - var lane = pickArbitraryLane(rootRenderLanes); - workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); + default: + return null; + } +} - var _update = createRootErrorUpdate(workInProgress, _errorInfo, lane); +function unwindInterruptedWork(interruptedWork, renderLanes) { + switch (interruptedWork.tag) { + case ClassComponent: { + var childContextTypes = interruptedWork.type.childContextTypes; - enqueueCapturedUpdate(workInProgress, _update); - return; + if (childContextTypes !== null && childContextTypes !== undefined) { + popContext(interruptedWork); } - case ClassComponent: - // Capture and retry - var errorInfo = value; - var ctor = workInProgress.type; - var instance = workInProgress.stateNode; + break; + } - if ( - (workInProgress.flags & DidCapture) === NoFlags && - (typeof ctor.getDerivedStateFromError === "function" || - (instance !== null && - typeof instance.componentDidCatch === "function" && - !isAlreadyFailedLegacyErrorBoundary(instance))) - ) { - workInProgress.flags |= ShouldCapture; + case HostRoot: { + popHostContainer(interruptedWork); + popTopLevelContextObject(interruptedWork); + resetWorkInProgressVersions(); + break; + } - var _lane = pickArbitraryLane(rootRenderLanes); + case HostComponent: { + popHostContext(interruptedWork); + break; + } - workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state + case HostPortal: + popHostContainer(interruptedWork); + break; - var _update2 = createClassErrorUpdate( - workInProgress, - errorInfo, - _lane - ); + case SuspenseComponent: + popSuspenseContext(interruptedWork); + break; - enqueueCapturedUpdate(workInProgress, _update2); - return; - } + case SuspenseListComponent: + popSuspenseContext(interruptedWork); + break; - break; - } + case ContextProvider: + var context = interruptedWork.type._context; + popProvider(context, interruptedWork); + break; - workInProgress = workInProgress.return; - } while (workInProgress !== null); + case OffscreenComponent: + case LegacyHiddenComponent: + popRenderLanes(interruptedWork); + + break; + } } var didWarnAboutUndefinedSnapshotBeforeUpdate = null; @@ -16762,11 +16978,21 @@ var didWarnAboutUndefinedSnapshotBeforeUpdate = null; var PossiblyWeakSet = typeof WeakSet === "function" ? WeakSet : Set; var nextEffect = null; // Used for Profiling builds to track updaters. +var inProgressLanes = null; +var inProgressRoot = null; + var callComponentWillUnmountWithTimer = function(current, instance) { instance.props = current.memoizedProps; instance.state = current.memoizedState; - { + if (current.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentWillUnmount(); + } finally { + recordLayoutEffectDuration(current); + } + } else { instance.componentWillUnmount(); } }; // Capture errors so they don't interrupt mounting. @@ -16798,7 +17024,11 @@ function safelyDetachRef(current, nearestMountedAncestor) { if (ref !== null) { if (typeof ref === "function") { { - { + if (current.mode & ProfileMode) { + startLayoutEffectTimer(); + invokeGuardedCallback(null, ref, null, null); + recordLayoutEffectDuration(current); + } else { invokeGuardedCallback(null, ref, null, null); } @@ -17105,6 +17335,58 @@ function commitHookEffectListMount(tag, finishedWork) { } } +function commitPassiveEffectDurations(finishedRoot, finishedWork) { + { + // Only Profilers with work in their subtree will have an Update effect scheduled. + if ((finishedWork.flags & Update) !== NoFlags) { + switch (finishedWork.tag) { + case Profiler: { + var passiveEffectDuration = + finishedWork.stateNode.passiveEffectDuration; + var _finishedWork$memoize = finishedWork.memoizedProps, + id = _finishedWork$memoize.id, + onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase. + // It does not get reset until the start of the next commit phase. + + var commitTime = getCommitTime(); + var phase = finishedWork.alternate === null ? "mount" : "update"; + + { + if (isCurrentUpdateNested()) { + phase = "nested-update"; + } + } + + if (typeof onPostCommit === "function") { + onPostCommit(id, phase, passiveEffectDuration, commitTime); + } // Bubble times to the next nearest ancestor Profiler. + // After we process that Profiler, we'll bubble further up. + + var parentFiber = finishedWork.return; + + outer: while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.passiveEffectDuration += passiveEffectDuration; + break outer; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.passiveEffectDuration += passiveEffectDuration; + break outer; + } + + parentFiber = parentFiber.return; + } + + break; + } + } + } + } +} + function commitLayoutEffectOnFiber( finishedRoot, current, @@ -17120,7 +17402,14 @@ function commitLayoutEffectOnFiber( // This is done to prevent sibling component effects from interfering with each other, // e.g. a destroy function in one component should never override a ref set // by a create function in another component during the same commit. - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + commitHookEffectListMount(Layout | HasEffect, finishedWork); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { commitHookEffectListMount(Layout | HasEffect, finishedWork); } @@ -17164,7 +17453,14 @@ function commitLayoutEffectOnFiber( } } - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentDidMount(); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { instance.componentDidMount(); } } else { @@ -17205,7 +17501,18 @@ function commitLayoutEffectOnFiber( } } - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate + ); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { instance.componentDidUpdate( prevProps, prevState, @@ -17316,6 +17623,12 @@ function commitLayoutEffectOnFiber( var commitTime = getCommitTime(); var phase = current === null ? "mount" : "update"; + { + if (isCurrentUpdateNested()) { + phase = "nested-update"; + } + } + if (typeof onRender === "function") { onRender( finishedWork.memoizedProps.id, @@ -17326,6 +17639,40 @@ function commitLayoutEffectOnFiber( commitTime ); } + + { + if (typeof onCommit === "function") { + onCommit( + finishedWork.memoizedProps.id, + phase, + effectDuration, + commitTime + ); + } // Schedule a passive effect for this Profiler to call onPostCommit hooks. + // This effect should be scheduled even if there is no onPostCommit callback for this Profiler, + // because the effect is also where times bubble to parent Profilers. + + enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor. + // Do not reset these values until the next render so DevTools has a chance to read them first. + + var parentFiber = finishedWork.return; + + outer: while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.effectDuration += effectDuration; + break outer; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.effectDuration += effectDuration; + break outer; + } + + parentFiber = parentFiber.return; + } + } } break; @@ -17374,7 +17721,14 @@ function commitAttachRef(finishedWork) { } // Moved outside to ensure DCE works with this flag if (typeof ref === "function") { - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + ref(instanceToUse); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { ref(instanceToUse); } } else { @@ -17398,7 +17752,14 @@ function commitDetachRef(current) { if (currentRef !== null) { if (typeof currentRef === "function") { - { + if (current.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + currentRef(null); + } finally { + recordLayoutEffectDuration(current); + } + } else { currentRef(null); } } else { @@ -17433,7 +17794,11 @@ function commitUnmount(finishedRoot, current, nearestMountedAncestor) { if (destroy !== undefined) { if ((tag & Layout) !== NoFlags$1) { - { + if (current.mode & ProfileMode) { + startLayoutEffectTimer(); + safelyCallDestroy(current, nearestMountedAncestor, destroy); + recordLayoutEffectDuration(current); + } else { safelyCallDestroy(current, nearestMountedAncestor, destroy); } } @@ -17631,7 +17996,18 @@ function commitWork(current, finishedWork) { // This prevents sibling component effects from interfering with each other, // e.g. a destroy function in one component should never override a ref set // by a create function in another component during the same commit. - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + commitHookEffectListUnmount( + Layout | HasEffect, + finishedWork, + finishedWork.return + ); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { commitHookEffectListUnmount( Layout | HasEffect, finishedWork, @@ -17701,6 +18077,19 @@ function attachSuspenseRetryListeners(finishedWork) { if (!retryCache.has(wakeable)) { retryCache.add(wakeable); + { + if (isDevToolsPresent) { + if (inProgressLanes !== null && inProgressRoot !== null) { + // If we have pending work still, associate the original updaters with it. + restorePendingUpdaters(inProgressRoot, inProgressLanes); + } else { + throw Error( + "Expected finished root and lanes to be set. This is a bug in React." + ); + } + } + } + wakeable.then(retry, retry); } }); @@ -17723,8 +18112,12 @@ function isSuspenseBoundaryBeingHidden(current, finishedWork) { } function commitMutationEffects(root, firstChild, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; nextEffect = firstChild; commitMutationEffects_begin(root); + inProgressLanes = null; + inProgressRoot = null; } function commitMutationEffects_begin(root) { @@ -17858,8 +18251,12 @@ function commitMutationEffectsOnFiber(finishedWork, root) { } function commitLayoutEffects(finishedWork, root, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; nextEffect = finishedWork; commitLayoutEffects_begin(finishedWork, root, committedLanes); + inProgressLanes = null; + inProgressRoot = null; } function commitLayoutEffects_begin(subtreeRoot, root, committedLanes) { @@ -17992,7 +18389,15 @@ function commitPassiveMountOnFiber(finishedRoot, finishedWork) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (finishedWork.mode & ProfileMode) { + startPassiveEffectTimer(); + + try { + commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); + } finally { + recordPassiveEffectDuration(finishedWork); + } + } else { commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); } @@ -18093,7 +18498,15 @@ function commitPassiveUnmountOnFiber(finishedWork) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (finishedWork.mode & ProfileMode) { + startPassiveEffectTimer(); + commitHookEffectListUnmount( + Passive$1 | HasEffect, + finishedWork, + finishedWork.return + ); + recordPassiveEffectDuration(finishedWork); + } else { commitHookEffectListUnmount( Passive$1 | HasEffect, finishedWork, @@ -18167,7 +18580,11 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber( case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (current.mode & ProfileMode) { + startPassiveEffectTimer(); + commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); + recordPassiveEffectDuration(current); + } else { commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); } @@ -18441,6 +18858,7 @@ var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfi var rootDoesHavePassiveEffects = false; var rootWithPendingPassiveEffects = null; var pendingPassiveEffectsLanes = NoLanes; +var pendingPassiveProfilerEffects = []; // Use these to prevent an infinite loop of nested updates var NESTED_UPDATE_LIMIT = 50; var nestedUpdateCount = 0; @@ -18478,6 +18896,20 @@ function requestUpdateLane(fiber) { if ((mode & ConcurrentMode) === NoMode) { return SyncLane; + } else if ( + (executionContext & RenderContext) !== NoContext && + workInProgressRootRenderLanes !== NoLanes + ) { + // This is a render phase update. These are not officially supported. The + // old behavior is to give this the same "thread" (lanes) as + // whatever is currently rendering. So if you call `setState` on a component + // that happens later in the same render, it will flush. Ideally, we want to + // remove the special case and treat them as if they came from an + // interleaved event. Regardless, this pattern is not officially supported. + // This behavior is only a fallback. The flag only exists until we can roll + // out the setState warning, since existing code might accidentally rely on + // the current behavior. + return pickArbitraryLane(workInProgressRootRenderLanes); } var isTransition = requestCurrentTransition() !== NoTransition; @@ -18507,7 +18939,7 @@ function requestUpdateLane(fiber) { if (updateLane !== NoLane) { return updateLane; - } // This update originated outside React. Ask the host environement for an + } // This update originated outside React. Ask the host environment for an // appropriate priority, based on the type of event. // // The opaque type returned by the host config is internally a lane, so we can @@ -18542,6 +18974,12 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { return null; } + { + if (isDevToolsPresent) { + addFiberToLanesMap(root, fiber, lane); + } + } // Mark that the root has a pending update. + markRootUpdated(root, lane, eventTime); if (root === workInProgressRoot) { @@ -18550,7 +18988,7 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render // phase update. In that case, we don't treat render phase updates as if // they were interleaved, for backwards compat reasons. - { + if ((executionContext & RenderContext) === NoContext) { workInProgressRootUpdatedLanes = mergeLanes( workInProgressRootUpdatedLanes, lane @@ -18662,7 +19100,7 @@ function isInterleavedUpdate(fiber, lane) { // then don't treat this as an interleaved update. This pattern is // accompanied by a warning but we haven't fully deprecated it yet. We can // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled. - deferRenderPhaseUpdateToNextBatch + (executionContext & RenderContext) === NoContext ); } // Use this function to schedule a task for a root. There's only one task per // root; if a task was already scheduled, we'll check to make sure the priority @@ -18773,6 +19211,9 @@ function ensureRootIsScheduled(root, currentTime) { // goes through Scheduler. function performConcurrentWorkOnRoot(root, didTimeout) { + { + resetNestedUpdateFlag(); + } // Since we know we're in a React event, we can clear the current // event time. The next update will compute a new event time. currentEventTime = NoTimestamp; @@ -18795,7 +19236,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) { // there's a new task, or that there's no remaining work on this root. return null; } - } // Determine the next expiration time to work on, using the fields stored + } // Determine the next lanes to work on, using the fields stored // on the root. var lanes = getNextLanes( @@ -19000,6 +19441,10 @@ function markRootSuspended$1(root, suspendedLanes) { // through Scheduler function performSyncWorkOnRoot(root) { + { + syncNestedUpdateFlag(); + } + if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) { throw Error("Should not already be working."); } @@ -19300,6 +19745,22 @@ function renderRootSync(root, lanes) { // and prepare a fresh one. Otherwise we'll continue where we left off. if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + + if (memoizedUpdaters.size > 0) { + restorePendingUpdaters(root, workInProgressRootRenderLanes); + memoizedUpdaters.clear(); + } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. + // If we bailout on this work, we'll move them back (like above). + // It's important to move them now in case the work spawns more work at the same priority with different updaters. + // That way we can keep the current update and future updates separate. + + movePendingFibersToMemoized(root, lanes); + } + } + prepareFreshStack(root, lanes); } @@ -19346,6 +19807,22 @@ function renderRootConcurrent(root, lanes) { // and prepare a fresh one. Otherwise we'll continue where we left off. if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + + if (memoizedUpdaters.size > 0) { + restorePendingUpdaters(root, workInProgressRootRenderLanes); + memoizedUpdaters.clear(); + } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. + // If we bailout on this work, we'll move them back (like above). + // It's important to move them now in case the work spawns more work at the same priority with different updaters. + // That way we can keep the current update and future updates separate. + + movePendingFibersToMemoized(root, lanes); + } + } + resetRenderTimer(); prepareFreshStack(root, lanes); } @@ -19446,7 +19923,7 @@ function completeUnitOfWork(unitOfWork) { // 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(completedWork); // Because this fiber did not complete, don't reset its expiration time. + var _next = unwindWork(completedWork); // Because this fiber did not complete, don't reset its lanes. if (_next !== null) { // If completing this work spawned new work, do that next. We'll come @@ -19591,7 +20068,7 @@ function commitRootImpl(root, renderPriorityLevel) { } } // Check if there are any effects in the whole tree. // TODO: This is left over from the effect list implementation, where we had - // to check for the existence of `firstEffect` to satsify Flow. I think the + // to check for the existence of `firstEffect` to satisfy Flow. I think the // only other reason this optimization exists is because it affects profiling. // Reconsider whether this is necessary. @@ -19630,7 +20107,7 @@ function commitRootImpl(root, renderPriorityLevel) { recordCommitTime(); } - commitMutationEffects(root, finishedWork); + commitMutationEffects(root, finishedWork, lanes); resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after // the mutation phase, so that the previous tree is still current during @@ -19683,6 +20160,9 @@ function commitRootImpl(root, renderPriorityLevel) { } if (includesSomeLane(remainingLanes, SyncLane)) { + { + markNestedUpdateScheduled(); + } // Count the number of times the root synchronously re-renders without // finishing. If there are too many, it indicates an infinite update loop. if (root === rootWithNestedUpdates) { @@ -19696,6 +20176,12 @@ function commitRootImpl(root, renderPriorityLevel) { } onCommitRoot(finishedWork.stateNode, renderPriorityLevel); + + { + if (isDevToolsPresent) { + root.memoizedUpdaters.clear(); + } + } // additional work on this root is scheduled. ensureRootIsScheduled(root, now()); @@ -19759,6 +20245,19 @@ function flushPassiveEffects() { return false; } +function enqueuePendingPassiveProfilerEffect(fiber) { + { + pendingPassiveProfilerEffects.push(fiber); + + if (!rootDoesHavePassiveEffects) { + rootDoesHavePassiveEffects = true; + scheduleCallback(NormalPriority, function() { + flushPassiveEffects(); + return null; + }); + } + } +} function flushPassiveEffectsImpl() { if (rootWithPendingPassiveEffects === null) { @@ -19785,6 +20284,16 @@ function flushPassiveEffectsImpl() { commitPassiveUnmountEffects(root.current); commitPassiveMountEffects(root, root.current); // TODO: Move to commitPassiveMountEffects + { + var profilerEffects = pendingPassiveProfilerEffects; + pendingPassiveProfilerEffects = []; + + for (var i = 0; i < profilerEffects.length; i++) { + var _fiber = profilerEffects[i]; + commitPassiveEffectDurations(root, _fiber); + } + } + { isFlushingPassiveEffects = false; } @@ -19802,6 +20311,12 @@ function flushPassiveEffectsImpl() { onPostCommitRoot(root); + { + var stateNode = root.current.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; + } + return true; } @@ -19950,7 +20465,7 @@ function retryTimedOutBoundary(boundaryFiber, retryLane) { // The boundary fiber (a Suspense component or SuspenseList component) // previously was rendered in its fallback state. One of the promises that // suspended it has resolved, which means at least part of the tree was - // likely unblocked. Try rendering again, at a new expiration time. + // likely unblocked. Try rendering again, at a new lanes. if (retryLane === NoLane) { // TODO: Assign this to `suspenseState.retryLane`? to avoid // unnecessary entanglement? @@ -20368,6 +20883,18 @@ function warnAboutRenderPhaseUpdatesInDEV(fiber) { } } } // a 'shared' variable that changes when act() opens/closes in tests. +function restorePendingUpdaters(root, lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + memoizedUpdaters.forEach(function(schedulingFiber) { + addFiberToLanesMap(root, schedulingFiber, lanes); + }); // This function intentionally does not clear memoized updaters. + // Those may still be relevant to the current commit + // and a future one (e.g. Suspense). + } + } +} var didWarnAboutUnmockedScheduler = false; // TODO Before we release concurrent mode, revisit this and decide whether a mocked // scheduler is the actual recommendation. The alternative could be a testing build, @@ -20868,8 +21395,6 @@ var hasBadMapPolyfill; } } -var debugCounter = 1; - function FiberNode(tag, pendingProps, key, mode) { // Instance this.tag = tag; @@ -20925,7 +21450,6 @@ function FiberNode(tag, pendingProps, key, mode) { { // This isn't directly used but is handy for debugging internals: - this._debugID = debugCounter++; this._debugSource = null; this._debugOwner = null; this._debugNeedsRemount = false; @@ -21005,7 +21529,6 @@ function createWorkInProgress(current, pendingProps) { { // DEV-only fields - workInProgress._debugID = current._debugID; workInProgress._debugSource = current._debugSource; workInProgress._debugOwner = current._debugOwner; workInProgress._debugHookTypes = current._debugHookTypes; @@ -21226,7 +21749,13 @@ function createFiberFromTypeAndProps( case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - mode |= StrictLegacyMode | StrictEffectsMode; + mode |= StrictLegacyMode; + + if ((mode & ConcurrentMode) !== NoMode) { + // Strict effects should never run on legacy roots + mode |= StrictEffectsMode; + } + break; case REACT_PROFILER_TYPE: @@ -21468,7 +21997,6 @@ function assignFiberPropertiesInDEV(target, source) { target.treeBaseDuration = source.treeBaseDuration; } - target._debugID = source._debugID; target._debugSource = source._debugSource; target._debugOwner = source._debugOwner; target._debugNeedsRemount = source._debugNeedsRemount; @@ -21500,6 +22028,20 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.entangledLanes = NoLanes; this.entanglements = createLaneMap(NoLanes); + { + this.effectDuration = 0; + this.passiveEffectDuration = 0; + } + + { + this.memoizedUpdaters = new Set(); + var pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []); + + for (var i = 0; i < TotalLanes; i++) { + pendingUpdatersLaneMap.push(new Set()); + } + } + { switch (tag) { case ConcurrentRoot: @@ -21764,6 +22306,14 @@ function getPublicRootInstance(container) { } } +var shouldErrorImpl = function(fiber) { + return null; +}; + +function shouldError(fiber) { + return shouldErrorImpl(fiber); +} + var shouldSuspendImpl = function(fiber) { return false; }; @@ -21778,6 +22328,7 @@ var overrideProps = null; var overridePropsDeletePath = null; var overridePropsRenamePath = null; var scheduleUpdate = null; +var setErrorHandler = null; var setSuspenseHandler = null; { @@ -21965,6 +22516,10 @@ var setSuspenseHandler = null; scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp); }; + setErrorHandler = function(newShouldErrorImpl) { + shouldErrorImpl = newShouldErrorImpl; + }; + setSuspenseHandler = function(newShouldSuspendImpl) { shouldSuspendImpl = newShouldSuspendImpl; }; @@ -22002,6 +22557,7 @@ function injectIntoDevTools(devToolsConfig) { overrideProps: overrideProps, overridePropsDeletePath: overridePropsDeletePath, overridePropsRenamePath: overridePropsRenamePath, + setErrorHandler: setErrorHandler, setSuspenseHandler: setSuspenseHandler, scheduleUpdate: scheduleUpdate, currentDispatcherRef: ReactCurrentDispatcher, diff --git a/Libraries/Renderer/implementations/ReactFabric-dev.js b/Libraries/Renderer/implementations/ReactFabric-dev.js index 6899cf9d416be1..972192aba65bf5 100644 --- a/Libraries/Renderer/implementations/ReactFabric-dev.js +++ b/Libraries/Renderer/implementations/ReactFabric-dev.js @@ -8,7 +8,7 @@ * @nolint * @providesModule ReactFabric-dev * @preventMunge - * @generated SignedSource<<64e2487c0986fc70b80db55f5a1cbb38>> + * @generated SignedSource<<74d609f93cda46b8478a82d095ab769c>> */ 'use strict'; @@ -2844,7 +2844,6 @@ var enableProfilerTimer = true; var enableLazyElements = false; var warnAboutStringRefs = false; var enableNewReconciler = false; -var deferRenderPhaseUpdateToNextBatch = true; var enableLazyContextPropagation = false; // Don't change these two values. They're used by React Dev Tools. @@ -4044,9 +4043,6 @@ var DebugTracingMode = var StrictLegacyMode = /* */ 8; -var StrictEffectsMode = - /* */ - 16; // If those values are changed that package should be rebuilt and redeployed. @@ -4679,6 +4675,48 @@ function markRootEntangled(root, entangledLanes) { lanes &= ~lane; } } +function addFiberToLanesMap(root, fiber, lanes) { + if (!isDevToolsPresent) { + return; + } + + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; + + while (lanes > 0) { + var index = laneToIndex(lanes); + var lane = 1 << index; + var updaters = pendingUpdatersLaneMap[index]; + updaters.add(fiber); + lanes &= ~lane; + } +} +function movePendingFibersToMemoized(root, lanes) { + if (!isDevToolsPresent) { + return; + } + + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; + var memoizedUpdaters = root.memoizedUpdaters; + + while (lanes > 0) { + var index = laneToIndex(lanes); + var lane = 1 << index; + var updaters = pendingUpdatersLaneMap[index]; + + if (updaters.size > 0) { + updaters.forEach(function(fiber) { + var alternate = fiber.alternate; + + if (alternate === null || !memoizedUpdaters.has(alternate)) { + memoizedUpdaters.add(fiber); + } + }); + updaters.clear(); + } + + lanes &= ~lane; + } +} var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros. Only used on lanes, so assume input is an integer. // Based on: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 @@ -5697,7 +5735,7 @@ var Passive$1 = /* */ 4; -var ReactVersion = "17.0.3-2d8d133e1"; +var ReactVersion = "17.0.3-0eea57724"; var ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig; var NoTransition = 0; @@ -6481,9 +6519,7 @@ function readContext(context) { lastContextDependency = contextItem; currentlyRenderingFiber.dependencies = { lanes: NoLanes, - firstContext: contextItem, - // TODO: This is an old field. Delete it. - responders: null + firstContext: contextItem }; } else { // Append a new context item. @@ -12053,7 +12089,52 @@ var InvalidNestedHooksDispatcherOnRerenderInDEV = null; var now$1 = Scheduler.unstable_now; var commitTime = 0; +var layoutEffectStartTime = -1; var profilerStartTime = -1; +var passiveEffectStartTime = -1; +/** + * Tracks whether the current update was a nested/cascading update (scheduled from a layout effect). + * + * The overall sequence is: + * 1. render + * 2. commit (and call `onRender`, `onCommit`) + * 3. check for nested updates + * 4. flush passive effects (and call `onPostCommit`) + * + * Nested updates are identified in step 3 above, + * but step 4 still applies to the work that was just committed. + * We use two flags to track nested updates then: + * one tracks whether the upcoming update is a nested update, + * and the other tracks whether the current update was a nested update. + * The first value gets synced to the second at the start of the render phase. + */ + +var currentUpdateIsNested = false; +var nestedUpdateScheduled = false; + +function isCurrentUpdateNested() { + return currentUpdateIsNested; +} + +function markNestedUpdateScheduled() { + { + nestedUpdateScheduled = true; + } +} + +function resetNestedUpdateFlag() { + { + currentUpdateIsNested = false; + nestedUpdateScheduled = false; + } +} + +function syncNestedUpdateFlag() { + { + currentUpdateIsNested = nestedUpdateScheduled; + nestedUpdateScheduled = false; + } +} function getCommitTime() { return commitTime; @@ -12088,6 +12169,77 @@ function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { } } +function recordLayoutEffectDuration(fiber) { + if (layoutEffectStartTime >= 0) { + var elapsedTime = now$1() - layoutEffectStartTime; + layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor + // Or the root (for the DevTools Profiler to read) + + var parentFiber = fiber.return; + + while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.effectDuration += elapsedTime; + return; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.effectDuration += elapsedTime; + return; + } + + parentFiber = parentFiber.return; + } + } +} + +function recordPassiveEffectDuration(fiber) { + if (passiveEffectStartTime >= 0) { + var elapsedTime = now$1() - passiveEffectStartTime; + passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor + // Or the root (for the DevTools Profiler to read) + + var parentFiber = fiber.return; + + while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + + if (root !== null) { + root.passiveEffectDuration += elapsedTime; + } + + return; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + + if (parentStateNode !== null) { + // Detached fibers have their state node cleared out. + // In this case, the return pointer is also cleared out, + // so we won't be able to report the time spent in this Profiler's subtree. + parentStateNode.passiveEffectDuration += elapsedTime; + } + + return; + } + + parentFiber = parentFiber.return; + } + } +} + +function startLayoutEffectTimer() { + layoutEffectStartTime = now$1(); +} + +function startPassiveEffectTimer() { + passiveEffectStartTime = now$1(); +} + function transferActualDuration(fiber) { // Transfer time spent rendering these children so we don't lose it // after we rerender. This is used as a helper in special cases @@ -12100,474 +12252,564 @@ function transferActualDuration(fiber) { } } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; -var didReceiveUpdate = false; -var didWarnAboutBadClass; -var didWarnAboutModulePatternComponent; -var didWarnAboutContextTypeOnFunctionComponent; -var didWarnAboutGetDerivedStateOnFunctionComponent; -var didWarnAboutFunctionRefs; -var didWarnAboutReassigningProps; -var didWarnAboutRevealOrder; -var didWarnAboutTailOptions; - -{ - didWarnAboutBadClass = {}; - didWarnAboutModulePatternComponent = {}; - didWarnAboutContextTypeOnFunctionComponent = {}; - didWarnAboutGetDerivedStateOnFunctionComponent = {}; - didWarnAboutFunctionRefs = {}; - didWarnAboutReassigningProps = false; - didWarnAboutRevealOrder = {}; - didWarnAboutTailOptions = {}; -} - -function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { - if (current === null) { - // If this is a fresh new component that hasn't been rendered yet, we - // won't update its child set by applying minimal side-effects. Instead, - // we will add them all to the child before it gets rendered. That means - // we can optimize this reconciliation pass by not tracking side-effects. - workInProgress.child = mountChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - } else { - // If the current child is the same as the work in progress, it means that - // we haven't yet started any work on these children. Therefore, we use - // the clone algorithm to create a copy of all the current children. - // If we had any progressed work already, that is invalid at this point so - // let's throw it out. - workInProgress.child = reconcileChildFibers( - workInProgress, - current.child, - nextChildren, - renderLanes - ); - } +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: getStackByFiberInDevAndProd(source) + }; } -function forceUnmountCurrentAndReconcile( - current, - workInProgress, - nextChildren, - renderLanes +if ( + !( + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog === + "function" + ) ) { - // This function is fork of reconcileChildren. It's used in cases where we - // want to reconcile without matching against the existing set. This has the - // effect of all current children being unmounted; even if the type and key - // are the same, the old child is unmounted and a new child is created. - // - // To do this, we're going to go through the reconcile algorithm twice. In - // the first pass, we schedule a deletion for all the current children by - // passing null. - workInProgress.child = reconcileChildFibers( - workInProgress, - current.child, - null, - renderLanes - ); // In the second pass, we mount the new children. The trick here is that we - // pass null in place of where we usually pass the current child set. This has - // the effect of remounting all children regardless of whether their - // identities match. + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +} - workInProgress.child = reconcileChildFibers( - workInProgress, - null, - nextChildren, - renderLanes +function showErrorDialog(boundary, errorInfo) { + var capturedError = { + componentStack: errorInfo.stack !== null ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + boundary !== null && boundary.tag === ClassComponent + ? boundary.stateNode + : null + }; + return ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog( + capturedError ); } -function updateForwardRef( - current, - workInProgress, - Component, - nextProps, - renderLanes -) { - // TODO: current can be non-null here even if the component - // hasn't yet mounted. This happens after the first render suspends. - // We'll need to figure out if this is fine or can cause issues. - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; +function logCapturedError(boundary, errorInfo) { + try { + var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. + // This enables renderers like ReactNative to better manage redbox behavior. - if (innerPropTypes) { - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(Component) - ); - } + if (logError === false) { + return; } - } - var render = Component.render; - var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent - - var nextChildren; - prepareToReadContext(workInProgress, renderLanes); + var error = errorInfo.value; - { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - nextChildren = renderWithHooks( - current, - workInProgress, - render, - nextProps, - ref, - renderLanes - ); + if (true) { + var source = errorInfo.source; + var stack = errorInfo.stack; + var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling + // `preventDefault()` in window `error` handler. + // We record this information as an expando on the error. - setIsRendering(false); - } + if (error != null && error._suppressLogging) { + if (boundary.tag === ClassComponent) { + // The error is recoverable and was silenced. + // Ignore it and don't print the stack addendum. + // This is handy for testing error boundaries without noise. + return; + } // The error is fatal. Since the silencing might have + // been accidental, we'll surface it anyway. + // However, the browser would have silenced the original error + // so we'll print it first, and then print the stack addendum. - if (current !== null && !didReceiveUpdate) { - bailoutHooks(current, workInProgress, renderLanes); - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } // React DevTools reads this flag. + console["error"](error); // Don't transform to our wrapper + // For a more detailed description of this block, see: + // https://github.com/facebook/react/pull/13384 + } - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; + var componentName = source ? getComponentNameFromFiber(source) : null; + var componentNameMessage = componentName + ? "The above error occurred in the <" + componentName + "> component:" + : "The above error occurred in one of your React components:"; + var errorBoundaryMessage; + + if (boundary.tag === HostRoot) { + errorBoundaryMessage = + "Consider adding an error boundary to your tree to customize error handling behavior.\n" + + "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries."; + } else { + var errorBoundaryName = + getComponentNameFromFiber(boundary) || "Anonymous"; + errorBoundaryMessage = + "React will try to recreate this component tree from scratch " + + ("using the error boundary you provided, " + errorBoundaryName + "."); + } + + var combinedMessage = + componentNameMessage + + "\n" + + 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); // Don't transform to our wrapper + } else { + // In production, we print the error directly. + // This will include the message, the JS stack, and anything the browser wants to show. + // We pass the error object instead of custom message so that the browser displays the error natively. + console["error"](error); // Don't transform to our wrapper + } + } catch (e) { + // This method must not throw, or React internal state will get messed up. + // If console.error is overridden, or logCapturedError() shows a dialog that throws, + // we want to report this error outside of the normal stack as a last resort. + // https://github.com/facebook/react/issues/13188 + setTimeout(function() { + throw e; + }); + } } -function updateMemoComponent( - current, - workInProgress, - Component, - nextProps, - updateLanes, - renderLanes -) { - if (current === null) { - var type = Component.type; +var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; - if ( - isSimpleFunctionComponent(type) && - Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. - Component.defaultProps === undefined - ) { - var resolvedType = type; +function createRootErrorUpdate(fiber, errorInfo, lane) { + var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. + + update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property + // being called "element". + + update.payload = { + element: null + }; + var error = errorInfo.value; + + update.callback = function() { + onUncaughtError(error); + logCapturedError(fiber, errorInfo); + }; + + return update; +} + +function createClassErrorUpdate(fiber, errorInfo, lane) { + var update = createUpdate(NoTimestamp, lane); + update.tag = CaptureUpdate; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + + if (typeof getDerivedStateFromError === "function") { + var error$1 = errorInfo.value; + + update.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error$1); + }; + } + + var inst = fiber.stateNode; + if (inst !== null && typeof inst.componentDidCatch === "function") { + update.callback = function callback() { { - resolvedType = resolveFunctionForHotReloading(type); - } // If this is a plain function component without default props, - // and with only the default shallow comparison, we upgrade it - // to a SimpleMemoComponent to allow fast path updates. + markFailedErrorBoundaryForHotReloading(fiber); + } - workInProgress.tag = SimpleMemoComponent; - workInProgress.type = resolvedType; + if (typeof getDerivedStateFromError !== "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 getDerivedStateFromError is + // not defined. + markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined + + logCapturedError(fiber, errorInfo); + } + + var error$1 = errorInfo.value; + var stack = errorInfo.stack; + this.componentDidCatch(error$1, { + componentStack: stack !== null ? stack : "" + }); { - validateFunctionComponentInDev(workInProgress, type); + if (typeof getDerivedStateFromError !== "function") { + // If componentDidCatch is the only error boundary method defined, + // then it needs to call setState to recover from errors. + // If no state update is scheduled then the boundary will swallow the error. + if (!includesSomeLane(fiber.lanes, SyncLane)) { + error( + "%s: Error boundaries should implement getDerivedStateFromError(). " + + "In that method, return a state update to display an error message or fallback UI.", + getComponentNameFromFiber(fiber) || "Unknown" + ); + } + } } + }; + } else { + update.callback = function() { + markFailedErrorBoundaryForHotReloading(fiber); + }; + } - return updateSimpleMemoComponent( - current, - workInProgress, - resolvedType, - nextProps, - updateLanes, - renderLanes - ); + return update; +} + +function attachPingListener(root, wakeable, lanes) { + // Attach a listener to the promise to "ping" the root and retry. But only if + // one does not already exist for the lanes we're currently rendering (which + // acts like a "thread ID" here). + var pingCache = root.pingCache; + var threadIDs; + + if (pingCache === null) { + pingCache = root.pingCache = new PossiblyWeakMap$1(); + threadIDs = new Set(); + pingCache.set(wakeable, threadIDs); + } else { + threadIDs = pingCache.get(wakeable); + + if (threadIDs === undefined) { + threadIDs = new Set(); + pingCache.set(wakeable, threadIDs); } + } - { - var innerPropTypes = type.propTypes; + if (!threadIDs.has(lanes)) { + // Memoize using the thread ID to prevent redundant listeners. + threadIDs.add(lanes); + var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); - if (innerPropTypes) { - // Inner memo component props aren't currently validated in createElement. - // We could move it there, but we'd still need this for lazy code path. - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(type) - ); + { + if (isDevToolsPresent) { + // If we have pending work still, restore the original updaters + restorePendingUpdaters(root, lanes); } } - var child = createFiberFromTypeAndProps( - Component.type, - null, - nextProps, - workInProgress, - workInProgress.mode, - renderLanes - ); - child.ref = workInProgress.ref; - child.return = workInProgress; - workInProgress.child = child; - return child; + wakeable.then(ping, ping); } +} - { - var _type = Component.type; - var _innerPropTypes = _type.propTypes; +function throwException( + root, + returnFiber, + sourceFiber, + value, + rootRenderLanes +) { + // The source fiber did not complete. + sourceFiber.flags |= Incomplete; - if (_innerPropTypes) { - // Inner memo component props aren't currently validated in createElement. - // We could move it there, but we'd still need this for lazy code path. - checkPropTypes( - _innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(_type) - ); + { + if (isDevToolsPresent) { + // If we have pending work still, restore the original updaters + restorePendingUpdaters(root, rootRenderLanes); } } - var currentChild = current.child; // This is always exactly one child + if ( + value !== null && + typeof value === "object" && + typeof value.then === "function" + ) { + var wakeable = value; + // A legacy mode Suspense quirk, only relevant to hook components. - if (!includesSomeLane(updateLanes, renderLanes)) { - // This will be the props with resolved defaultProps, - // unlike current.memoizedProps which will be the unresolved ones. - var prevProps = currentChild.memoizedProps; // Default to shallow comparison + var tag = sourceFiber.tag; - var compare = Component.compare; - compare = compare !== null ? compare : shallowEqual; + if ( + (sourceFiber.mode & ConcurrentMode) === NoMode && + (tag === FunctionComponent || + tag === ForwardRef || + tag === SimpleMemoComponent) + ) { + var currentSource = sourceFiber.alternate; - if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + if (currentSource) { + sourceFiber.updateQueue = currentSource.updateQueue; + sourceFiber.memoizedState = currentSource.memoizedState; + sourceFiber.lanes = currentSource.lanes; + } else { + sourceFiber.updateQueue = null; + sourceFiber.memoizedState = null; + } } - } // React DevTools reads this flag. - workInProgress.flags |= PerformedWork; - var newChild = createWorkInProgress(currentChild, nextProps); - newChild.ref = workInProgress.ref; - newChild.return = workInProgress; - workInProgress.child = newChild; - return newChild; -} + var hasInvisibleParentBoundary = hasSuspenseContext( + suspenseStackCursor.current, + InvisibleParentSuspenseContext + ); // Schedule the nearest Suspense to re-render the timed out view. -function updateSimpleMemoComponent( - current, - workInProgress, - Component, - nextProps, - updateLanes, - renderLanes -) { - // TODO: current can be non-null here even if the component - // hasn't yet mounted. This happens when the inner render suspends. - // We'll need to figure out if this is fine or can cause issues. - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var outerMemoType = workInProgress.elementType; + var _workInProgress = returnFiber; - if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { - // We warn when you define propTypes on lazy() - // so let's just skip over it to find memo() outer wrapper. - // Inner props for memo are validated later. - var lazyComponent = outerMemoType; - var payload = lazyComponent._payload; - var init = lazyComponent._init; + do { + if ( + _workInProgress.tag === SuspenseComponent && + shouldCaptureSuspense(_workInProgress, hasInvisibleParentBoundary) + ) { + // Found the nearest boundary. + // Stash the promise on the boundary fiber. If the boundary times out, we'll + // attach another listener to flip the boundary back to its normal state. + var wakeables = _workInProgress.updateQueue; - try { - outerMemoType = init(payload); - } catch (x) { - outerMemoType = null; - } // Inner propTypes will be validated in the function component path. + if (wakeables === null) { + var updateQueue = new Set(); + updateQueue.add(wakeable); + _workInProgress.updateQueue = updateQueue; + } else { + wakeables.add(wakeable); + } // If the boundary is in legacy mode, we should *not* + // suspend the commit. Pretend as if the suspended component rendered + // null and keep rendering. In the commit phase, we'll schedule a + // subsequent synchronous update to re-render the Suspense. + // + // Note: It doesn't matter whether the component that suspended was + // inside a concurrent mode tree. If the Suspense is outside of it, we + // should *not* suspend the commit. + // + // If the suspense boundary suspended itself suspended, we don't have to + // do this trick because nothing was partially started. We can just + // directly do a second pass over the fallback in this render and + // pretend we meant to render that directly. - var outerPropTypes = outerMemoType && outerMemoType.propTypes; + if ( + (_workInProgress.mode & ConcurrentMode) === NoMode && + _workInProgress !== returnFiber + ) { + _workInProgress.flags |= DidCapture; + sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. + // But we shouldn't call any lifecycle methods or callbacks. Remove + // all lifecycle effect tags. - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - nextProps, // Resolved (SimpleMemoComponent has no defaultProps) - "prop", - getComponentNameFromType(outerMemoType) - ); - } - } - } - } + sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); - if (current !== null) { - var prevProps = current.memoizedProps; + if (sourceFiber.tag === ClassComponent) { + var _currentSourceFiber = sourceFiber.alternate; - if ( - shallowEqual(prevProps, nextProps) && - current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload. - workInProgress.type === current.type - ) { - didReceiveUpdate = false; + if (_currentSourceFiber === null) { + // This is a new mount. Change the tag so it's not mistaken for a + // completed class component. For example, we should not call + // componentWillUnmount if it is deleted. + sourceFiber.tag = IncompleteClassComponent; + } else { + // When we try rendering again, we should not reuse the current fiber, + // since it's known to be in an inconsistent state. Use a force update to + // prevent a bail out. + var update = createUpdate(NoTimestamp, SyncLane); + update.tag = ForceUpdate; + enqueueUpdate(sourceFiber, update); + } + } // The source fiber did not complete. Mark it with Sync priority to + // indicate that it still has pending work. - if (!includesSomeLane(renderLanes, updateLanes)) { - // The pending lanes were cleared at the beginning of beginWork. We're - // about to bail out, but there might be other lanes that weren't - // included in the current render. Usually, the priority level of the - // remaining updates is accumlated during the evaluation of the - // component (i.e. when processing the update queue). But since since - // we're bailing out early *without* evaluating the component, we need - // to account for it here, too. Reset to the value of the current fiber. - // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, - // because a MemoComponent fiber does not have hooks or an update queue; - // rather, it wraps around an inner component, which may or may not - // contains hooks. - // TODO: Move the reset at in beginWork out of the common path so that - // this is no longer necessary. - workInProgress.lanes = current.lanes; - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); - } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { - // This is a special case that only exists for legacy mode. - // See https://github.com/facebook/react/pull/19216. - didReceiveUpdate = true; - } - } - } + sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); // Exit without suspending. - return updateFunctionComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes - ); -} + return; + } // Confirmed that the boundary is in a concurrent mode tree. Continue + // with the normal suspend path. + // + // After this we'll use a set of heuristics to determine whether this + // render pass will run to completion or restart or "suspend" the commit. + // The actual logic for this is spread out in different places. + // + // This first principle is that if we're going to suspend when we complete + // a root, then we should also restart if we get an update or ping that + // might unsuspend it, and vice versa. The only reason to suspend is + // because you think you might want to restart before committing. However, + // it doesn't make sense to restart only while in the period we're suspended. + // + // Restarting too aggressively is also not good because it starves out any + // intermediate loading state. So we use heuristics to determine when. + // Suspense Heuristics + // + // If nothing threw a Promise or all the same fallbacks are already showing, + // then don't suspend/restart. + // + // If this is an initial render of a new tree of Suspense boundaries and + // those trigger a fallback, then don't suspend/restart. We want to ensure + // that we can show the initial loading state as quickly as possible. + // + // If we hit a "Delayed" case, such as when we'd switch from content back into + // a fallback, then we should always suspend/restart. Transitions apply + // to this case. If none is defined, JND is used instead. + // + // If we're already showing a fallback and it gets "retried", allowing us to show + // another level, but there's still an inner boundary that would show a fallback, + // then we suspend/restart for 500ms since the last time we showed a fallback + // anywhere in the tree. This effectively throttles progressive loading into a + // consistent train of commits. This also gives us an opportunity to restart to + // get to the completed state slightly earlier. + // + // If there's ambiguity due to batching it's resolved in preference of: + // 1) "delayed", 2) "initial render", 3) "retry". + // + // We want to ensure that a "busy" state doesn't get force committed. We want to + // ensure that new initial loading states can commit as soon as possible. -function updateOffscreenComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; - var nextChildren = nextProps.children; - var prevState = current !== null ? current.memoizedState : null; // If this is not null, this is a cache pool that was carried over from the - // previous render. We will push this to the cache pool context so that we can - // resume in-flight requests. + attachPingListener(root, wakeable, rootRenderLanes); + _workInProgress.flags |= ShouldCapture; + _workInProgress.lanes = rootRenderLanes; + return; + } // This boundary already captured during this render. Continue to the next + // boundary. - var spawnedCachePool = null; + _workInProgress = _workInProgress.return; + } while (_workInProgress !== null); // No boundary was found. Fallthrough to error mode. + // TODO: Use invariant so the message is stripped in prod? - if ( - nextProps.mode === "hidden" || - nextProps.mode === "unstable-defer-without-hiding" - ) { - // Rendering a hidden tree. - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - // In legacy sync mode, don't defer the subtree. Render it now. - var nextState = { - baseLanes: NoLanes, - cachePool: null - }; - workInProgress.memoizedState = nextState; - pushRenderLanes(workInProgress, renderLanes); - } else if (!includesSomeLane(renderLanes, OffscreenLane)) { - // We're hidden, and we're not rendering at Offscreen. We will bail out - // and resume this tree later. - var nextBaseLanes; + value = new Error( + (getComponentNameFromFiber(sourceFiber) || "A React component") + + " suspended while rendering, but no fallback UI was specified.\n" + + "\n" + + "Add a component higher in the tree to " + + "provide a loading indicator or placeholder to display." + ); + } // We didn't find a boundary that could handle this type of exception. Start + // over and traverse parent path again, this time treating the exception + // as an error. - if (prevState !== null) { - var prevBaseLanes = prevState.baseLanes; - nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); - } else { - nextBaseLanes = renderLanes; - } // Schedule this fiber to re-render at offscreen priority. Then bailout. + renderDidError(); + value = createCapturedValue(value, sourceFiber); + var workInProgress = returnFiber; - workInProgress.lanes = workInProgress.childLanes = laneToLanes( - OffscreenLane - ); - var _nextState = { - baseLanes: nextBaseLanes, - cachePool: spawnedCachePool - }; - workInProgress.memoizedState = _nextState; - workInProgress.updateQueue = null; // We're about to bail out, but we need to push this to the stack anyway - // to avoid a push/pop misalignment. + do { + switch (workInProgress.tag) { + case HostRoot: { + var _errorInfo = value; + workInProgress.flags |= ShouldCapture; + var lane = pickArbitraryLane(rootRenderLanes); + workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); - pushRenderLanes(workInProgress, nextBaseLanes); + var _update = createRootErrorUpdate(workInProgress, _errorInfo, lane); - return null; - } else { - var _nextState2 = { - baseLanes: NoLanes, - cachePool: null - }; - workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. + enqueueCapturedUpdate(workInProgress, _update); + return; + } - var subtreeRenderLanes = - prevState !== null ? prevState.baseLanes : renderLanes; - pushRenderLanes(workInProgress, subtreeRenderLanes); - } - } else { - // Rendering a visible tree. - var _subtreeRenderLanes; + case ClassComponent: + // Capture and retry + var errorInfo = value; + var ctor = workInProgress.type; + var instance = workInProgress.stateNode; - if (prevState !== null) { - // We're going from hidden -> visible. - _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); + if ( + (workInProgress.flags & DidCapture) === NoFlags && + (typeof ctor.getDerivedStateFromError === "function" || + (instance !== null && + typeof instance.componentDidCatch === "function" && + !isAlreadyFailedLegacyErrorBoundary(instance))) + ) { + workInProgress.flags |= ShouldCapture; - workInProgress.memoizedState = null; - } else { - // We weren't previously hidden, and we still aren't, so there's nothing - // special to do. Need to push to the stack regardless, though, to avoid - // a push/pop misalignment. - _subtreeRenderLanes = renderLanes; - } + var _lane = pickArbitraryLane(rootRenderLanes); - pushRenderLanes(workInProgress, _subtreeRenderLanes); - } + workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} // Note: These happen to have identical begin phases, for now. We shouldn't hold -// ourselves to this constraint, though. If the behavior diverges, we should -// fork the function. + var _update2 = createClassErrorUpdate( + workInProgress, + errorInfo, + _lane + ); -var updateLegacyHiddenComponent = updateOffscreenComponent; + enqueueCapturedUpdate(workInProgress, _update2); + return; + } -function updateFragment(current, workInProgress, renderLanes) { - var nextChildren = workInProgress.pendingProps; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} + break; + } -function updateMode(current, workInProgress, renderLanes) { - var nextChildren = workInProgress.pendingProps.children; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; + workInProgress = workInProgress.return; + } while (workInProgress !== null); } -function updateProfiler(current, workInProgress, renderLanes) { - { - workInProgress.flags |= Update; - } +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var didReceiveUpdate = false; +var didWarnAboutBadClass; +var didWarnAboutModulePatternComponent; +var didWarnAboutContextTypeOnFunctionComponent; +var didWarnAboutGetDerivedStateOnFunctionComponent; +var didWarnAboutFunctionRefs; +var didWarnAboutReassigningProps; +var didWarnAboutRevealOrder; +var didWarnAboutTailOptions; - var nextProps = workInProgress.pendingProps; - var nextChildren = nextProps.children; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; +{ + didWarnAboutBadClass = {}; + didWarnAboutModulePatternComponent = {}; + didWarnAboutContextTypeOnFunctionComponent = {}; + didWarnAboutGetDerivedStateOnFunctionComponent = {}; + didWarnAboutFunctionRefs = {}; + didWarnAboutReassigningProps = false; + didWarnAboutRevealOrder = {}; + didWarnAboutTailOptions = {}; } -function markRef(current, workInProgress) { - var ref = workInProgress.ref; - - if ( - (current === null && ref !== null) || - (current !== null && current.ref !== ref) - ) { - // Schedule a Ref effect - workInProgress.flags |= Ref; +function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { + if (current === null) { + // If this is a fresh new component that hasn't been rendered yet, we + // won't update its child set by applying minimal side-effects. Instead, + // we will add them all to the child before it gets rendered. That means + // we can optimize this reconciliation pass by not tracking side-effects. + workInProgress.child = mountChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); + } else { + // If the current child is the same as the work in progress, it means that + // we haven't yet started any work on these children. Therefore, we use + // the clone algorithm to create a copy of all the current children. + // If we had any progressed work already, that is invalid at this point so + // let's throw it out. + workInProgress.child = reconcileChildFibers( + workInProgress, + current.child, + nextChildren, + renderLanes + ); } } -function updateFunctionComponent( +function forceUnmountCurrentAndReconcile( + current, + workInProgress, + nextChildren, + renderLanes +) { + // This function is fork of reconcileChildren. It's used in cases where we + // want to reconcile without matching against the existing set. This has the + // effect of all current children being unmounted; even if the type and key + // are the same, the old child is unmounted and a new child is created. + // + // To do this, we're going to go through the reconcile algorithm twice. In + // the first pass, we schedule a deletion for all the current children by + // passing null. + workInProgress.child = reconcileChildFibers( + workInProgress, + current.child, + null, + renderLanes + ); // In the second pass, we mount the new children. The trick here is that we + // pass null in place of where we usually pass the current child set. This has + // the effect of remounting all children regardless of whether their + // identities match. + + workInProgress.child = reconcileChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); +} + +function updateForwardRef( current, workInProgress, Component, nextProps, renderLanes ) { + // TODO: current can be non-null here even if the component + // hasn't yet mounted. This happens after the first render suspends. + // We'll need to figure out if this is fine or can cause issues. { if (workInProgress.type !== workInProgress.elementType) { // Lazy component props can't be validated in createElement @@ -12585,12 +12827,8 @@ function updateFunctionComponent( } } - var context; - - { - var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); - context = getMaskedContext(workInProgress, unmaskedContext); - } + var render = Component.render; + var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent var nextChildren; prepareToReadContext(workInProgress, renderLanes); @@ -12601,9 +12839,9 @@ function updateFunctionComponent( nextChildren = renderWithHooks( current, workInProgress, - Component, + render, nextProps, - context, + ref, renderLanes ); @@ -12620,432 +12858,453 @@ function updateFunctionComponent( return workInProgress.child; } -function updateClassComponent( +function updateMemoComponent( current, workInProgress, Component, nextProps, + updateLanes, renderLanes ) { - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; + if (current === null) { + var type = Component.type; + + if ( + isSimpleFunctionComponent(type) && + Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. + Component.defaultProps === undefined + ) { + var resolvedType = type; + + { + resolvedType = resolveFunctionForHotReloading(type); + } // If this is a plain function component without default props, + // and with only the default shallow comparison, we upgrade it + // to a SimpleMemoComponent to allow fast path updates. + + workInProgress.tag = SimpleMemoComponent; + workInProgress.type = resolvedType; + + { + validateFunctionComponentInDev(workInProgress, type); + } + + return updateSimpleMemoComponent( + current, + workInProgress, + resolvedType, + nextProps, + updateLanes, + renderLanes + ); + } + + { + var innerPropTypes = type.propTypes; if (innerPropTypes) { + // Inner memo component props aren't currently validated in createElement. + // We could move it there, but we'd still need this for lazy code path. checkPropTypes( innerPropTypes, nextProps, // Resolved props "prop", - getComponentNameFromType(Component) + getComponentNameFromType(type) ); } } - } // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. - var hasContext; + var child = createFiberFromTypeAndProps( + Component.type, + null, + nextProps, + workInProgress, + workInProgress.mode, + renderLanes + ); + child.ref = workInProgress.ref; + child.return = workInProgress; + workInProgress.child = child; + return child; + } - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); - } else { - hasContext = false; + { + var _type = Component.type; + var _innerPropTypes = _type.propTypes; + + if (_innerPropTypes) { + // Inner memo component props aren't currently validated in createElement. + // We could move it there, but we'd still need this for lazy code path. + checkPropTypes( + _innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(_type) + ); + } } - prepareToReadContext(workInProgress, renderLanes); - var instance = workInProgress.stateNode; - var shouldUpdate; + var currentChild = current.child; // This is always exactly one child - if (instance === null) { - if (current !== null) { - // A class component without an instance only mounts if it suspended - // inside a non-concurrent tree, in an inconsistent state. We want to - // treat it like a new mount, even though an empty version of it already - // committed. Disconnect the alternate pointers. - current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + if (!includesSomeLane(updateLanes, renderLanes)) { + // This will be the props with resolved defaultProps, + // unlike current.memoizedProps which will be the unresolved ones. + var prevProps = currentChild.memoizedProps; // Default to shallow comparison - workInProgress.flags |= Placement; - } // In the initial pass we might need to construct the instance. + var compare = Component.compare; + compare = compare !== null ? compare : shallowEqual; - constructClassInstance(workInProgress, Component, nextProps); - mountClassInstance(workInProgress, Component, nextProps, renderLanes); - shouldUpdate = true; - } else if (current === null) { - // In a resume, we'll already have an instance we can reuse. - shouldUpdate = resumeMountClassInstance( - workInProgress, - Component, - nextProps, - renderLanes - ); - } else { - shouldUpdate = updateClassInstance( - current, - workInProgress, - Component, - nextProps, - renderLanes - ); + if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } + } // React DevTools reads this flag. + + workInProgress.flags |= PerformedWork; + var newChild = createWorkInProgress(currentChild, nextProps); + newChild.ref = workInProgress.ref; + newChild.return = workInProgress; + workInProgress.child = newChild; + return newChild; +} + +function updateSimpleMemoComponent( + current, + workInProgress, + Component, + nextProps, + updateLanes, + renderLanes +) { + // TODO: current can be non-null here even if the component + // hasn't yet mounted. This happens when the inner render suspends. + // We'll need to figure out if this is fine or can cause issues. + { + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var outerMemoType = workInProgress.elementType; + + if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { + // We warn when you define propTypes on lazy() + // so let's just skip over it to find memo() outer wrapper. + // Inner props for memo are validated later. + var lazyComponent = outerMemoType; + var payload = lazyComponent._payload; + var init = lazyComponent._init; + + try { + outerMemoType = init(payload); + } catch (x) { + outerMemoType = null; + } // Inner propTypes will be validated in the function component path. + + var outerPropTypes = outerMemoType && outerMemoType.propTypes; + + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + nextProps, // Resolved (SimpleMemoComponent has no defaultProps) + "prop", + getComponentNameFromType(outerMemoType) + ); + } + } + } + } + + if (current !== null) { + var prevProps = current.memoizedProps; + + if ( + shallowEqual(prevProps, nextProps) && + current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload. + workInProgress.type === current.type + ) { + didReceiveUpdate = false; + + if (!includesSomeLane(renderLanes, updateLanes)) { + // The pending lanes were cleared at the beginning of beginWork. We're + // about to bail out, but there might be other lanes that weren't + // included in the current render. Usually, the priority level of the + // remaining updates is accumulated during the evaluation of the + // component (i.e. when processing the update queue). But since since + // we're bailing out early *without* evaluating the component, we need + // to account for it here, too. Reset to the value of the current fiber. + // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, + // because a MemoComponent fiber does not have hooks or an update queue; + // rather, it wraps around an inner component, which may or may not + // contains hooks. + // TODO: Move the reset at in beginWork out of the common path so that + // this is no longer necessary. + workInProgress.lanes = current.lanes; + return bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); + } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { + // This is a special case that only exists for legacy mode. + // See https://github.com/facebook/react/pull/19216. + didReceiveUpdate = true; + } + } } - var nextUnitOfWork = finishClassComponent( + return updateFunctionComponent( current, workInProgress, Component, - shouldUpdate, - hasContext, + nextProps, renderLanes ); - - { - var inst = workInProgress.stateNode; - - if (shouldUpdate && inst.props !== nextProps) { - if (!didWarnAboutReassigningProps) { - error( - "It looks like %s is reassigning its own `this.props` while rendering. " + - "This is not supported and can lead to confusing bugs.", - getComponentNameFromFiber(workInProgress) || "a component" - ); - } - - didWarnAboutReassigningProps = true; - } - } - - return nextUnitOfWork; } -function finishClassComponent( - current, - workInProgress, - Component, - shouldUpdate, - hasContext, - renderLanes -) { - // Refs should update even if shouldComponentUpdate returns false - markRef(current, workInProgress); - var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; - - if (!shouldUpdate && !didCaptureError) { - // Context providers should defer to sCU for rendering - if (hasContext) { - invalidateContextProvider(workInProgress, Component, false); - } - - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } - - var instance = workInProgress.stateNode; // Rerender +function updateOffscreenComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; + var nextChildren = nextProps.children; + var prevState = current !== null ? current.memoizedState : null; // If this is not null, this is a cache pool that was carried over from the + // previous render. We will push this to the cache pool context so that we can + // resume in-flight requests. - ReactCurrentOwner$1.current = workInProgress; - var nextChildren; + var spawnedCachePool = null; if ( - didCaptureError && - typeof Component.getDerivedStateFromError !== "function" + nextProps.mode === "hidden" || + nextProps.mode === "unstable-defer-without-hiding" ) { - // If we captured an error, but getDerivedStateFromError 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; + // Rendering a hidden tree. + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + // In legacy sync mode, don't defer the subtree. Render it now. + var nextState = { + baseLanes: NoLanes, + cachePool: null + }; + workInProgress.memoizedState = nextState; + pushRenderLanes(workInProgress, renderLanes); + } else if (!includesSomeLane(renderLanes, OffscreenLane)) { + // We're hidden, and we're not rendering at Offscreen. We will bail out + // and resume this tree later. + var nextBaseLanes; - { - stopProfilerTimerIfRunning(); - } - } else { - { - setIsRendering(true); - nextChildren = instance.render(); + if (prevState !== null) { + var prevBaseLanes = prevState.baseLanes; + nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); + } else { + nextBaseLanes = renderLanes; + } // Schedule this fiber to re-render at offscreen priority. Then bailout. - setIsRendering(false); - } - } // React DevTools reads this flag. + workInProgress.lanes = workInProgress.childLanes = laneToLanes( + OffscreenLane + ); + var _nextState = { + baseLanes: nextBaseLanes, + cachePool: spawnedCachePool + }; + workInProgress.memoizedState = _nextState; + workInProgress.updateQueue = null; // We're about to bail out, but we need to push this to the stack anyway + // to avoid a push/pop misalignment. - workInProgress.flags |= PerformedWork; + pushRenderLanes(workInProgress, nextBaseLanes); - if (current !== null && didCaptureError) { - // If we're recovering from an error, reconcile without reusing any of - // the existing children. Conceptually, the normal children and the children - // that are shown on error are two different sets, so we shouldn't reuse - // normal children even if their identities match. - forceUnmountCurrentAndReconcile( - current, - workInProgress, - nextChildren, - renderLanes - ); + return null; + } else { + var _nextState2 = { + baseLanes: NoLanes, + cachePool: null + }; + workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. + + var subtreeRenderLanes = + prevState !== null ? prevState.baseLanes : renderLanes; + pushRenderLanes(workInProgress, subtreeRenderLanes); + } } else { - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - } // Memoize state using the values we just used to render. - // TODO: Restructure so we never read values from the instance. + // Rendering a visible tree. + var _subtreeRenderLanes; - workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. + if (prevState !== null) { + // We're going from hidden -> visible. + _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); - if (hasContext) { - invalidateContextProvider(workInProgress, Component, true); + workInProgress.memoizedState = null; + } else { + // We weren't previously hidden, and we still aren't, so there's nothing + // special to do. Need to push to the stack regardless, though, to avoid + // a push/pop misalignment. + _subtreeRenderLanes = renderLanes; + } + + pushRenderLanes(workInProgress, _subtreeRenderLanes); } + reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; -} - -function pushHostRootContext(workInProgress) { - var root = workInProgress.stateNode; +} // Note: These happen to have identical begin phases, for now. We shouldn't hold +// ourselves to this constraint, though. If the behavior diverges, we should +// fork the function. - if (root.pendingContext) { - pushTopLevelContextObject( - workInProgress, - root.pendingContext, - root.pendingContext !== root.context - ); - } else if (root.context) { - // Should always be set - pushTopLevelContextObject(workInProgress, root.context, false); - } +var updateLegacyHiddenComponent = updateOffscreenComponent; - pushHostContainer(workInProgress, root.containerInfo); +function updateFragment(current, workInProgress, renderLanes) { + var nextChildren = workInProgress.pendingProps; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function updateHostRoot(current, workInProgress, renderLanes) { - pushHostRootContext(workInProgress); - var updateQueue = workInProgress.updateQueue; - - if (!(current !== null && updateQueue !== null)) { - throw Error( - "If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue." - ); - } - - var nextProps = workInProgress.pendingProps; - var prevState = workInProgress.memoizedState; - var prevChildren = prevState.element; - cloneUpdateQueue(current, workInProgress); - processUpdateQueue(workInProgress, nextProps, null, renderLanes); - var nextState = workInProgress.memoizedState; - var root = workInProgress.stateNode; - // being called "element". - - var nextChildren = nextState.element; - - if (nextChildren === prevChildren) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } - - if (root.hydrate && enterHydrationState()) { - var child = mountChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - workInProgress.child = child; - var node = child; - - while (node) { - // Mark each child as hydrating. This is a fast path to know whether this - // tree is part of a hydrating tree. This is used to determine if a child - // node has fully mounted yet, and for scheduling event replaying. - // Conceptually this is similar to Placement in that a new subtree is - // inserted into the React tree here. It just happens to not need DOM - // mutations because it already exists. - node.flags = (node.flags & ~Placement) | Hydrating; - node = node.sibling; - } - } else { - // Otherwise reset hydration state in case we aborted and resumed another - // root. - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - } - +function updateMode(current, workInProgress, renderLanes) { + var nextChildren = workInProgress.pendingProps.children; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } -function updateHostComponent(current, workInProgress, renderLanes) { - pushHostContext(workInProgress); - - var type = workInProgress.type; - var nextProps = workInProgress.pendingProps; - var prevProps = current !== null ? current.memoizedProps : null; - var nextChildren = nextProps.children; +function updateProfiler(current, workInProgress, renderLanes) { + { + workInProgress.flags |= Update; - if (prevProps !== null && shouldSetTextContent()) { - // If we're switching from a direct text child to a normal child, or to - // empty, we need to schedule the text content to be reset. - workInProgress.flags |= ContentReset; + { + // Reset effect durations for the next eventual effect phase. + // These are reset during render to allow the DevTools commit hook a chance to read them, + var stateNode = workInProgress.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; + } } - markRef(current, workInProgress); + var nextProps = workInProgress.pendingProps; + var nextChildren = nextProps.children; reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } -function updateHostText(current, workInProgress) { - // immediately after. +function markRef(current, workInProgress) { + var ref = workInProgress.ref; - return null; + if ( + (current === null && ref !== null) || + (current !== null && current.ref !== ref) + ) { + // Schedule a Ref effect + workInProgress.flags |= Ref; + } } -function mountLazyComponent( - _current, +function updateFunctionComponent( + current, workInProgress, - elementType, - updateLanes, + Component, + nextProps, renderLanes ) { - if (_current !== null) { - // A lazy component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - - workInProgress.flags |= Placement; - } - - var props = workInProgress.pendingProps; - var lazyComponent = elementType; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - var Component = init(payload); // Store the unwrapped component in the type. - - workInProgress.type = Component; - var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); - var resolvedProps = resolveDefaultProps(Component, props); - var child; - - switch (resolvedTag) { - case FunctionComponent: { - { - validateFunctionComponentInDev(workInProgress, Component); - workInProgress.type = Component = resolveFunctionForHotReloading( - Component - ); - } - - child = updateFunctionComponent( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } - - case ClassComponent: { - { - workInProgress.type = Component = resolveClassForHotReloading( - Component - ); - } - - child = updateClassComponent( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } + { + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; - case ForwardRef: { - { - workInProgress.type = Component = resolveForwardRefForHotReloading( - Component + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) ); } - - child = updateForwardRef( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } - - case MemoComponent: { - { - if (workInProgress.type !== workInProgress.elementType) { - var outerPropTypes = Component.propTypes; - - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - resolvedProps, // Resolved for outer only - "prop", - getComponentNameFromType(Component) - ); - } - } - } - - child = updateMemoComponent( - null, - workInProgress, - Component, - resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too - updateLanes, - renderLanes - ); - return child; } } - var hint = ""; + var context; { - if ( - Component !== null && - typeof Component === "object" && - Component.$$typeof === REACT_LAZY_TYPE - ) { - hint = " Did you wrap a component in React.lazy() more than once?"; - } - } // This message intentionally doesn't mention ForwardRef or MemoComponent - // because the fact that it's a separate type of work is an - // implementation detail. + var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); + context = getMaskedContext(workInProgress, unmaskedContext); + } + + var nextChildren; + prepareToReadContext(workInProgress, renderLanes); { - throw Error( - "Element type is invalid. Received a promise that resolves to: " + - Component + - ". Lazy element type must resolve to a class or function." + - hint + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + nextChildren = renderWithHooks( + current, + workInProgress, + Component, + nextProps, + context, + renderLanes ); + + setIsRendering(false); } + + if (current !== null && !didReceiveUpdate) { + bailoutHooks(current, workInProgress, renderLanes); + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } // React DevTools reads this flag. + + workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function mountIncompleteClassComponent( - _current, +function updateClassComponent( + current, workInProgress, Component, nextProps, renderLanes ) { - if (_current !== null) { - // An incomplete component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + { + // This is used by DevTools to force a boundary to error. + switch (shouldError(workInProgress)) { + case false: { + var _instance = workInProgress.stateNode; + var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack. + // Is there a better way to do this? + + var tempInstance = new ctor( + workInProgress.memoizedProps, + _instance.context + ); + var state = tempInstance.state; - workInProgress.flags |= Placement; - } // Promote the fiber to a class and try rendering again. + _instance.updater.enqueueSetState(_instance, state, null); - workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` - // Push context providers early to prevent context stack mismatches. + break; + } + + case true: { + workInProgress.flags |= DidCapture; + workInProgress.flags |= ShouldCapture; + var error$1 = new Error("Simulated error coming from DevTools"); + var lane = pickArbitraryLane(renderLanes); + workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state + + var update = createClassErrorUpdate( + workInProgress, + createCapturedValue(error$1, workInProgress), + lane + ); + enqueueCapturedUpdate(workInProgress, update); + break; + } + } + + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; + + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) + ); + } + } + } // Push context providers early to prevent context stack mismatches. // During mounting we don't know the child context yet as the instance doesn't exist. // We will invalidate the child context in finishClassComponent() right after rendering. @@ -13059,3421 +13318,3379 @@ function mountIncompleteClassComponent( } prepareToReadContext(workInProgress, renderLanes); - constructClassInstance(workInProgress, Component, nextProps); - mountClassInstance(workInProgress, Component, nextProps, renderLanes); - return finishClassComponent( - null, + var instance = workInProgress.stateNode; + var shouldUpdate; + + if (instance === null) { + if (current !== null) { + // A class component without an instance only mounts if it suspended + // inside a non-concurrent tree, in an inconsistent state. We want to + // treat it like a new mount, even though an empty version of it already + // committed. Disconnect the alternate pointers. + current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + + workInProgress.flags |= Placement; + } // In the initial pass we might need to construct the instance. + + constructClassInstance(workInProgress, Component, nextProps); + mountClassInstance(workInProgress, Component, nextProps, renderLanes); + shouldUpdate = true; + } else if (current === null) { + // In a resume, we'll already have an instance we can reuse. + shouldUpdate = resumeMountClassInstance( + workInProgress, + Component, + nextProps, + renderLanes + ); + } else { + shouldUpdate = updateClassInstance( + current, + workInProgress, + Component, + nextProps, + renderLanes + ); + } + + var nextUnitOfWork = finishClassComponent( + current, workInProgress, Component, - true, + shouldUpdate, hasContext, renderLanes ); + + { + var inst = workInProgress.stateNode; + + if (shouldUpdate && inst.props !== nextProps) { + if (!didWarnAboutReassigningProps) { + error( + "It looks like %s is reassigning its own `this.props` while rendering. " + + "This is not supported and can lead to confusing bugs.", + getComponentNameFromFiber(workInProgress) || "a component" + ); + } + + didWarnAboutReassigningProps = true; + } + } + + return nextUnitOfWork; } -function mountIndeterminateComponent( - _current, +function finishClassComponent( + current, workInProgress, Component, + shouldUpdate, + hasContext, renderLanes ) { - if (_current !== null) { - // An indeterminate component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - - workInProgress.flags |= Placement; - } + // Refs should update even if shouldComponentUpdate returns false + markRef(current, workInProgress); + var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; - var props = workInProgress.pendingProps; - var context; + if (!shouldUpdate && !didCaptureError) { + // Context providers should defer to sCU for rendering + if (hasContext) { + invalidateContextProvider(workInProgress, Component, false); + } - { - var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); - context = getMaskedContext(workInProgress, unmaskedContext); + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } - prepareToReadContext(workInProgress, renderLanes); - var value; + var instance = workInProgress.stateNode; // Rerender - { - if ( - Component.prototype && - typeof Component.prototype.render === "function" - ) { - var componentName = getComponentNameFromType(Component) || "Unknown"; + ReactCurrentOwner$1.current = workInProgress; + var nextChildren; - if (!didWarnAboutBadClass[componentName]) { - error( - "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + - "This is likely to cause errors. Change %s to extend React.Component instead.", - componentName, - componentName - ); + if ( + didCaptureError && + typeof Component.getDerivedStateFromError !== "function" + ) { + // If we captured an error, but getDerivedStateFromError 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; - didWarnAboutBadClass[componentName] = true; - } + { + stopProfilerTimerIfRunning(); } + } else { + { + setIsRendering(true); + nextChildren = instance.render(); - if (workInProgress.mode & StrictLegacyMode) { - ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); + setIsRendering(false); } + } // React DevTools reads this flag. - setIsRendering(true); - ReactCurrentOwner$1.current = workInProgress; - value = renderWithHooks( - null, + workInProgress.flags |= PerformedWork; + + if (current !== null && didCaptureError) { + // If we're recovering from an error, reconcile without reusing any of + // the existing children. Conceptually, the normal children and the children + // that are shown on error are two different sets, so we shouldn't reuse + // normal children even if their identities match. + forceUnmountCurrentAndReconcile( + current, workInProgress, - Component, - props, - context, + nextChildren, renderLanes ); - setIsRendering(false); - } // React DevTools reads this flag. + } else { + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } // Memoize state using the values we just used to render. + // TODO: Restructure so we never read values from the instance. - workInProgress.flags |= PerformedWork; + workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. - { - // Support for module components is deprecated and is removed behind a flag. - // Whether or not it would crash later, we want to show a good message in DEV first. - if ( - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined - ) { - var _componentName = getComponentNameFromType(Component) || "Unknown"; + if (hasContext) { + invalidateContextProvider(workInProgress, Component, true); + } - if (!didWarnAboutModulePatternComponent[_componentName]) { - error( - "The <%s /> component appears to be a function component that returns a class instance. " + - "Change %s to a class that extends React.Component instead. " + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - "cannot be called with `new` by React.", - _componentName, - _componentName, - _componentName - ); + return workInProgress.child; +} - didWarnAboutModulePatternComponent[_componentName] = true; - } - } - } +function pushHostRootContext(workInProgress) { + var root = workInProgress.stateNode; - if ( - // Run these checks in production only if the flag is off. - // Eventually we'll delete this branch altogether. - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined - ) { - { - var _componentName2 = getComponentNameFromType(Component) || "Unknown"; + if (root.pendingContext) { + pushTopLevelContextObject( + workInProgress, + root.pendingContext, + root.pendingContext !== root.context + ); + } else if (root.context) { + // Should always be set + pushTopLevelContextObject(workInProgress, root.context, false); + } - if (!didWarnAboutModulePatternComponent[_componentName2]) { - error( - "The <%s /> component appears to be a function component that returns a class instance. " + - "Change %s to a class that extends React.Component instead. " + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - "cannot be called with `new` by React.", - _componentName2, - _componentName2, - _componentName2 - ); + pushHostContainer(workInProgress, root.containerInfo); +} - didWarnAboutModulePatternComponent[_componentName2] = true; - } - } // Proceed under the assumption that this is a class instance +function updateHostRoot(current, workInProgress, renderLanes) { + pushHostRootContext(workInProgress); + var updateQueue = workInProgress.updateQueue; - workInProgress.tag = ClassComponent; // Throw out any hooks that were used. + if (!(current !== null && updateQueue !== null)) { + throw Error( + "If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue." + ); + } - workInProgress.memoizedState = null; - workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. + var nextProps = workInProgress.pendingProps; + var prevState = workInProgress.memoizedState; + var prevChildren = prevState.element; + cloneUpdateQueue(current, workInProgress); + processUpdateQueue(workInProgress, nextProps, null, renderLanes); + var nextState = workInProgress.memoizedState; + var root = workInProgress.stateNode; + // being called "element". - var hasContext = false; + var nextChildren = nextState.element; - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); - } else { - hasContext = false; - } + if (nextChildren === prevChildren) { + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } - workInProgress.memoizedState = - value.state !== null && value.state !== undefined ? value.state : null; - initializeUpdateQueue(workInProgress); - adoptClassInstance(workInProgress, value); - mountClassInstance(workInProgress, Component, props, renderLanes); - return finishClassComponent( - null, + if (root.hydrate && enterHydrationState()) { + var child = mountChildFibers( workInProgress, - Component, - true, - hasContext, + null, + nextChildren, renderLanes ); + workInProgress.child = child; + var node = child; + + while (node) { + // Mark each child as hydrating. This is a fast path to know whether this + // tree is part of a hydrating tree. This is used to determine if a child + // node has fully mounted yet, and for scheduling event replaying. + // Conceptually this is similar to Placement in that a new subtree is + // inserted into the React tree here. It just happens to not need DOM + // mutations because it already exists. + node.flags = (node.flags & ~Placement) | Hydrating; + node = node.sibling; + } } else { - // Proceed under the assumption that this is a function component - workInProgress.tag = FunctionComponent; + // Otherwise reset hydration state in case we aborted and resumed another + // root. + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } - reconcileChildren(null, workInProgress, value, renderLanes); + return workInProgress.child; +} + +function updateHostComponent(current, workInProgress, renderLanes) { + pushHostContext(workInProgress); - { - validateFunctionComponentInDev(workInProgress, Component); - } + var type = workInProgress.type; + var nextProps = workInProgress.pendingProps; + var prevProps = current !== null ? current.memoizedProps : null; + var nextChildren = nextProps.children; - return workInProgress.child; + if (prevProps !== null && shouldSetTextContent()) { + // If we're switching from a direct text child to a normal child, or to + // empty, we need to schedule the text content to be reset. + workInProgress.flags |= ContentReset; } + + markRef(current, workInProgress); + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function validateFunctionComponentInDev(workInProgress, Component) { - { - if (Component) { - if (Component.childContextTypes) { - error( - "%s(...): childContextTypes cannot be defined on a function component.", - Component.displayName || Component.name || "Component" - ); - } - } +function updateHostText(current, workInProgress) { + // immediately after. - if (workInProgress.ref !== null) { - var info = ""; - var ownerName = getCurrentFiberOwnerNameInDevOrNull(); + return null; +} - if (ownerName) { - info += "\n\nCheck the render method of `" + ownerName + "`."; - } +function mountLazyComponent( + _current, + workInProgress, + elementType, + updateLanes, + renderLanes +) { + if (_current !== null) { + // A lazy component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - var warningKey = ownerName || workInProgress._debugID || ""; - var debugSource = workInProgress._debugSource; + workInProgress.flags |= Placement; + } - if (debugSource) { - warningKey = debugSource.fileName + ":" + debugSource.lineNumber; - } + var props = workInProgress.pendingProps; + var lazyComponent = elementType; + var payload = lazyComponent._payload; + var init = lazyComponent._init; + var Component = init(payload); // Store the unwrapped component in the type. - if (!didWarnAboutFunctionRefs[warningKey]) { - didWarnAboutFunctionRefs[warningKey] = true; + workInProgress.type = Component; + var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); + var resolvedProps = resolveDefaultProps(Component, props); + var child; - error( - "Function components cannot be given refs. " + - "Attempts to access this ref will fail. " + - "Did you mean to use React.forwardRef()?%s", - info + switch (resolvedTag) { + case FunctionComponent: { + { + validateFunctionComponentInDev(workInProgress, Component); + workInProgress.type = Component = resolveFunctionForHotReloading( + Component ); } - } - if (typeof Component.getDerivedStateFromProps === "function") { - var _componentName3 = getComponentNameFromType(Component) || "Unknown"; + child = updateFunctionComponent( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; + } - if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { - error( - "%s: Function components do not support getDerivedStateFromProps.", - _componentName3 + case ClassComponent: { + { + workInProgress.type = Component = resolveClassForHotReloading( + Component ); - - didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; } - } - if ( - typeof Component.contextType === "object" && - Component.contextType !== null - ) { - var _componentName4 = getComponentNameFromType(Component) || "Unknown"; + child = updateClassComponent( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; + } - if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { - error( - "%s: Function components do not support contextType.", - _componentName4 + case ForwardRef: { + { + workInProgress.type = Component = resolveForwardRefForHotReloading( + Component ); + } - didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; + child = updateForwardRef( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; + } + + case MemoComponent: { + { + if (workInProgress.type !== workInProgress.elementType) { + var outerPropTypes = Component.propTypes; + + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + resolvedProps, // Resolved for outer only + "prop", + getComponentNameFromType(Component) + ); + } + } } + + child = updateMemoComponent( + null, + workInProgress, + Component, + resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too + updateLanes, + renderLanes + ); + return child; } } -} -var SUSPENDED_MARKER = { - dehydrated: null, - retryLane: NoLane -}; - -function mountSuspenseOffscreenState(renderLanes) { - return { - baseLanes: renderLanes, - cachePool: getSuspendedCachePool() - }; -} + var hint = ""; -function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { - var cachePool = null; + { + if ( + Component !== null && + typeof Component === "object" && + Component.$$typeof === REACT_LAZY_TYPE + ) { + hint = " Did you wrap a component in React.lazy() more than once?"; + } + } // This message intentionally doesn't mention ForwardRef or MemoComponent + // because the fact that it's a separate type of work is an + // implementation detail. - return { - baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), - cachePool: cachePool - }; -} // TODO: Probably should inline this back + { + throw Error( + "Element type is invalid. Received a promise that resolves to: " + + Component + + ". Lazy element type must resolve to a class or function." + + hint + ); + } +} -function shouldRemainOnFallback( - suspenseContext, - current, +function mountIncompleteClassComponent( + _current, workInProgress, + Component, + nextProps, renderLanes ) { - // If we're already showing a fallback, there are cases where we need to - // remain on that fallback regardless of whether the content has resolved. - // For example, SuspenseList coordinates when nested content appears. - if (current !== null) { - var suspenseState = current.memoizedState; + if (_current !== null) { + // An incomplete component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - if (suspenseState === null) { - // Currently showing content. Don't hide it, even if ForceSuspenseFallack - // is true. More precise name might be "ForceRemainSuspenseFallback". - // Note: This is a factoring smell. Can't remain on a fallback if there's - // no fallback to remain on. - return false; - } - } // Not currently showing content. Consult the Suspense context. + workInProgress.flags |= Placement; + } // Promote the fiber to a class and try rendering again. - return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); -} + workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` + // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. -function getRemainingWorkInPrimaryTree(current, renderLanes) { - // TODO: Should not remove render lanes that were pinged during this render - return removeLanes(current.childLanes, renderLanes); + var hasContext; + + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; + } + + prepareToReadContext(workInProgress, renderLanes); + constructClassInstance(workInProgress, Component, nextProps); + mountClassInstance(workInProgress, Component, nextProps, renderLanes); + return finishClassComponent( + null, + workInProgress, + Component, + true, + hasContext, + renderLanes + ); } -function updateSuspenseComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. +function mountIndeterminateComponent( + _current, + workInProgress, + Component, + renderLanes +) { + if (_current !== null) { + // An indeterminate component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - { - if (shouldSuspend(workInProgress)) { - workInProgress.flags |= DidCapture; - } + workInProgress.flags |= Placement; } - var suspenseContext = suspenseStackCursor.current; - var showFallback = false; - var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; + var props = workInProgress.pendingProps; + var context; - if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { - // Something in this boundary's subtree already suspended. Switch to - // rendering the fallback children. - showFallback = true; - workInProgress.flags &= ~DidCapture; - } else { - // Attempting the main content - if (current === null || current.memoizedState !== null) { - // This is a new mount or this boundary is already showing a fallback state. - // Mark this subtree context as having at least one invisible parent that could - // handle the fallback state. - // Boundaries without fallbacks or should be avoided are not considered since - // they cannot handle preferred fallback states. - if ( - nextProps.fallback !== undefined && - nextProps.unstable_avoidThisFallback !== true - ) { - suspenseContext = addSubtreeSuspenseContext( - suspenseContext, - InvisibleParentSuspenseContext - ); - } - } + { + var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); + context = getMaskedContext(workInProgress, unmaskedContext); } - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense - // boundary's children. This involves some custom reconcilation logic. Two - // main reasons this is so complicated. - // - // First, Legacy Mode has different semantics for backwards compatibility. The - // primary tree will commit in an inconsistent state, so when we do the - // second pass to render the fallback, we do some exceedingly, uh, clever - // hacks to make that not totally break. Like transferring effects and - // deletions from hidden tree. In Concurrent Mode, it's much simpler, - // because we bailout on the primary tree completely and leave it in its old - // state, no effects. Same as what we do for Offscreen (except that - // Offscreen doesn't have the first render pass). - // - // Second is hydration. During hydration, the Suspense fiber has a slightly - // different layout, where the child points to a dehydrated fragment, which - // contains the DOM rendered by the server. - // - // Third, even if you set all that aside, Suspense is like error boundaries in - // that we first we try to render one tree, and if that fails, we render again - // and switch to a different tree. Like a try/catch block. So we have to track - // which branch we're currently rendering. Ideally we would model this using - // a stack. - - if (current === null) { - // Initial mount - // If we're currently hydrating, try to hydrate this boundary. - // But only if this has a fallback. - if (nextProps.fallback !== undefined); + prepareToReadContext(workInProgress, renderLanes); + var value; - var nextPrimaryChildren = nextProps.children; - var nextFallbackChildren = nextProps.fallback; + { + if ( + Component.prototype && + typeof Component.prototype.render === "function" + ) { + var componentName = getComponentNameFromType(Component) || "Unknown"; - if (showFallback) { - var fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ); - var primaryChildFragment = workInProgress.child; - primaryChildFragment.memoizedState = mountSuspenseOffscreenState( - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; - return fallbackFragment; - } else if (typeof nextProps.unstable_expectedLoadTime === "number") { - // This is a CPU-bound tree. Skip this tree and show a placeholder to - // unblock the surrounding content. Then immediately retry after the - // initial commit. - var _fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ); + if (!didWarnAboutBadClass[componentName]) { + error( + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + + "This is likely to cause errors. Change %s to extend React.Component instead.", + componentName, + componentName + ); - var _primaryChildFragment = workInProgress.child; - _primaryChildFragment.memoizedState = mountSuspenseOffscreenState( - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; // Since nothing actually suspended, there will nothing to ping this to - // get it started back up to attempt the next item. While in terms of - // priority this work has the same priority as this current render, it's - // not part of the same transition once the transition has committed. If - // it's sync, we still want to yield so that it can be painted. - // Conceptually, this is really the same as pinging. We can use any - // RetryLane even if it's the one currently rendering since we're leaving - // it behind on this node. + didWarnAboutBadClass[componentName] = true; + } + } - workInProgress.lanes = SomeRetryLane; - return _fallbackFragment; - } else { - return mountSuspensePrimaryChildren( - workInProgress, - nextPrimaryChildren, - renderLanes - ); + if (workInProgress.mode & StrictLegacyMode) { + ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); } - } else { - // This is an update. - // If the current fiber has a SuspenseState, that means it's already showing - // a fallback. - var prevState = current.memoizedState; - if (prevState !== null) { - if (showFallback) { - var _nextFallbackChildren2 = nextProps.fallback; - var _nextPrimaryChildren2 = nextProps.children; + setIsRendering(true); + ReactCurrentOwner$1.current = workInProgress; + value = renderWithHooks( + null, + workInProgress, + Component, + props, + context, + renderLanes + ); + setIsRendering(false); + } // React DevTools reads this flag. - var _fallbackChildFragment = updateSuspenseFallbackChildren( - current, - workInProgress, - _nextPrimaryChildren2, - _nextFallbackChildren2, - renderLanes - ); + workInProgress.flags |= PerformedWork; - var _primaryChildFragment3 = workInProgress.child; - var prevOffscreenState = current.child.memoizedState; - _primaryChildFragment3.memoizedState = - prevOffscreenState === null - ? mountSuspenseOffscreenState(renderLanes) - : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); - _primaryChildFragment3.childLanes = getRemainingWorkInPrimaryTree( - current, - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; - return _fallbackChildFragment; - } else { - var _nextPrimaryChildren3 = nextProps.children; + { + // Support for module components is deprecated and is removed behind a flag. + // Whether or not it would crash later, we want to show a good message in DEV first. + if ( + typeof value === "object" && + value !== null && + typeof value.render === "function" && + value.$$typeof === undefined + ) { + var _componentName = getComponentNameFromType(Component) || "Unknown"; - var _primaryChildFragment4 = updateSuspensePrimaryChildren( - current, - workInProgress, - _nextPrimaryChildren3, - renderLanes + if (!didWarnAboutModulePatternComponent[_componentName]) { + error( + "The <%s /> component appears to be a function component that returns a class instance. " + + "Change %s to a class that extends React.Component instead. " + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + + "cannot be called with `new` by React.", + _componentName, + _componentName, + _componentName ); - workInProgress.memoizedState = null; - return _primaryChildFragment4; + didWarnAboutModulePatternComponent[_componentName] = true; } - } else { - // The current tree is not already showing a fallback. - if (showFallback) { - // Timed out. - var _nextFallbackChildren3 = nextProps.fallback; - var _nextPrimaryChildren4 = nextProps.children; - - var _fallbackChildFragment2 = updateSuspenseFallbackChildren( - current, - workInProgress, - _nextPrimaryChildren4, - _nextFallbackChildren3, - renderLanes - ); - - var _primaryChildFragment5 = workInProgress.child; - var _prevOffscreenState = current.child.memoizedState; - _primaryChildFragment5.memoizedState = - _prevOffscreenState === null - ? mountSuspenseOffscreenState(renderLanes) - : updateSuspenseOffscreenState(_prevOffscreenState, renderLanes); - _primaryChildFragment5.childLanes = getRemainingWorkInPrimaryTree( - current, - renderLanes - ); // Skip the primary children, and continue working on the - // fallback children. + } + } - workInProgress.memoizedState = SUSPENDED_MARKER; - return _fallbackChildFragment2; - } else { - // Still haven't timed out. Continue rendering the children, like we - // normally do. - var _nextPrimaryChildren5 = nextProps.children; + if ( + // Run these checks in production only if the flag is off. + // Eventually we'll delete this branch altogether. + typeof value === "object" && + value !== null && + typeof value.render === "function" && + value.$$typeof === undefined + ) { + { + var _componentName2 = getComponentNameFromType(Component) || "Unknown"; - var _primaryChildFragment6 = updateSuspensePrimaryChildren( - current, - workInProgress, - _nextPrimaryChildren5, - renderLanes + if (!didWarnAboutModulePatternComponent[_componentName2]) { + error( + "The <%s /> component appears to be a function component that returns a class instance. " + + "Change %s to a class that extends React.Component instead. " + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + + "cannot be called with `new` by React.", + _componentName2, + _componentName2, + _componentName2 ); - workInProgress.memoizedState = null; - return _primaryChildFragment6; + didWarnAboutModulePatternComponent[_componentName2] = true; } - } - } -} + } // Proceed under the assumption that this is a class instance -function mountSuspensePrimaryChildren( - workInProgress, - primaryChildren, - renderLanes -) { - var mode = workInProgress.mode; - var primaryChildProps = { - mode: "visible", - children: primaryChildren - }; - var primaryChildFragment = createFiberFromOffscreen( - primaryChildProps, - mode, - renderLanes, - null - ); - primaryChildFragment.return = workInProgress; - workInProgress.child = primaryChildFragment; - return primaryChildFragment; -} + workInProgress.tag = ClassComponent; // Throw out any hooks that were used. -function mountSuspenseFallbackChildren( - workInProgress, - primaryChildren, - fallbackChildren, - renderLanes -) { - var mode = workInProgress.mode; - var progressedPrimaryFragment = workInProgress.child; - var primaryChildProps = { - mode: "hidden", - children: primaryChildren - }; - var primaryChildFragment; - var fallbackChildFragment; + workInProgress.memoizedState = null; + workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. - if ( - (mode & ConcurrentMode) === NoMode && - progressedPrimaryFragment !== null - ) { - // In legacy mode, we commit the primary tree as if it successfully - // completed, even though it's in an inconsistent state. - primaryChildFragment = progressedPrimaryFragment; - primaryChildFragment.childLanes = NoLanes; - primaryChildFragment.pendingProps = primaryChildProps; + var hasContext = false; - if (workInProgress.mode & ProfileMode) { - // Reset the durations from the first pass so they aren't included in the - // final amounts. This seems counterintuitive, since we're intentionally - // not measuring part of the render phase, but this makes it match what we - // do in Concurrent Mode. - primaryChildFragment.actualDuration = 0; - primaryChildFragment.actualStartTime = -1; - primaryChildFragment.selfBaseDuration = 0; - primaryChildFragment.treeBaseDuration = 0; + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; } - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null + workInProgress.memoizedState = + value.state !== null && value.state !== undefined ? value.state : null; + initializeUpdateQueue(workInProgress); + adoptClassInstance(workInProgress, value); + mountClassInstance(workInProgress, Component, props, renderLanes); + return finishClassComponent( + null, + workInProgress, + Component, + true, + hasContext, + renderLanes ); } else { - primaryChildFragment = createFiberFromOffscreen( - primaryChildProps, - mode, - NoLanes, - null - ); - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null - ); - } - - primaryChildFragment.return = workInProgress; - fallbackChildFragment.return = workInProgress; - primaryChildFragment.sibling = fallbackChildFragment; - workInProgress.child = primaryChildFragment; - return fallbackChildFragment; -} + // Proceed under the assumption that this is a function component + workInProgress.tag = FunctionComponent; -function createWorkInProgressOffscreenFiber(current, offscreenProps) { - // The props argument to `createWorkInProgress` is `any` typed, so we use this - // wrapper function to constrain it. - return createWorkInProgress(current, offscreenProps); -} + reconcileChildren(null, workInProgress, value, renderLanes); -function updateSuspensePrimaryChildren( - current, - workInProgress, - primaryChildren, - renderLanes -) { - var currentPrimaryChildFragment = current.child; - var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; - var primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, { - mode: "visible", - children: primaryChildren + validateFunctionComponentInDev(workInProgress, Component); } - ); - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - primaryChildFragment.lanes = renderLanes; + return workInProgress.child; } +} - primaryChildFragment.return = workInProgress; - primaryChildFragment.sibling = null; +function validateFunctionComponentInDev(workInProgress, Component) { + { + if (Component) { + if (Component.childContextTypes) { + error( + "%s(...): childContextTypes cannot be defined on a function component.", + Component.displayName || Component.name || "Component" + ); + } + } - if (currentFallbackChildFragment !== null) { - // Delete the fallback child fragment - var deletions = workInProgress.deletions; + if (workInProgress.ref !== null) { + var info = ""; + var ownerName = getCurrentFiberOwnerNameInDevOrNull(); - if (deletions === null) { - workInProgress.deletions = [currentFallbackChildFragment]; - workInProgress.flags |= ChildDeletion; - } else { - deletions.push(currentFallbackChildFragment); - } - } + if (ownerName) { + info += "\n\nCheck the render method of `" + ownerName + "`."; + } - workInProgress.child = primaryChildFragment; - return primaryChildFragment; -} + var warningKey = ownerName || ""; + var debugSource = workInProgress._debugSource; -function updateSuspenseFallbackChildren( - current, - workInProgress, - primaryChildren, - fallbackChildren, - renderLanes -) { - var mode = workInProgress.mode; - var currentPrimaryChildFragment = current.child; - var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; - var primaryChildProps = { - mode: "hidden", - children: primaryChildren - }; - var primaryChildFragment; + if (debugSource) { + warningKey = debugSource.fileName + ":" + debugSource.lineNumber; + } - if ( - // In legacy mode, we commit the primary tree as if it successfully - // completed, even though it's in an inconsistent state. - (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was - // already cloned. In legacy mode, the only case where this isn't true is - // when DevTools forces us to display a fallback; we skip the first render - // pass entirely and go straight to rendering the fallback. (In Concurrent - // Mode, SuspenseList can also trigger this scenario, but this is a legacy- - // only codepath.) - workInProgress.child !== currentPrimaryChildFragment - ) { - var progressedPrimaryFragment = workInProgress.child; - primaryChildFragment = progressedPrimaryFragment; - primaryChildFragment.childLanes = NoLanes; - primaryChildFragment.pendingProps = primaryChildProps; + if (!didWarnAboutFunctionRefs[warningKey]) { + didWarnAboutFunctionRefs[warningKey] = true; - if (workInProgress.mode & ProfileMode) { - // Reset the durations from the first pass so they aren't included in the - // final amounts. This seems counterintuitive, since we're intentionally - // not measuring part of the render phase, but this makes it match what we - // do in Concurrent Mode. - primaryChildFragment.actualDuration = 0; - primaryChildFragment.actualStartTime = -1; - primaryChildFragment.selfBaseDuration = - currentPrimaryChildFragment.selfBaseDuration; - primaryChildFragment.treeBaseDuration = - currentPrimaryChildFragment.treeBaseDuration; - } // The fallback fiber was added as a deletion during the first pass. - // However, since we're going to remain on the fallback, we no longer want - // to delete it. + error( + "Function components cannot be given refs. " + + "Attempts to access this ref will fail. " + + "Did you mean to use React.forwardRef()?%s", + info + ); + } + } - workInProgress.deletions = null; - } else { - primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, - primaryChildProps - ); // Since we're reusing a current tree, we need to reuse the flags, too. - // (We don't do this in legacy mode, because in legacy mode we don't re-use - // the current tree; see previous branch.) + if (typeof Component.getDerivedStateFromProps === "function") { + var _componentName3 = getComponentNameFromType(Component) || "Unknown"; + + if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { + error( + "%s: Function components do not support getDerivedStateFromProps.", + _componentName3 + ); - primaryChildFragment.subtreeFlags = - currentPrimaryChildFragment.subtreeFlags & StaticMask; - } + didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; + } + } - var fallbackChildFragment; + if ( + typeof Component.contextType === "object" && + Component.contextType !== null + ) { + var _componentName4 = getComponentNameFromType(Component) || "Unknown"; - if (currentFallbackChildFragment !== null) { - fallbackChildFragment = createWorkInProgress( - currentFallbackChildFragment, - fallbackChildren - ); - } else { - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null - ); // Needs a placement effect because the parent (the Suspense boundary) already - // mounted but this is a new fiber. + if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { + error( + "%s: Function components do not support contextType.", + _componentName4 + ); - fallbackChildFragment.flags |= Placement; + didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; + } + } } - - fallbackChildFragment.return = workInProgress; - primaryChildFragment.return = workInProgress; - primaryChildFragment.sibling = fallbackChildFragment; - workInProgress.child = primaryChildFragment; - return fallbackChildFragment; } -function scheduleWorkOnFiber(fiber, renderLanes) { - fiber.lanes = mergeLanes(fiber.lanes, renderLanes); - var alternate = fiber.alternate; - - if (alternate !== null) { - alternate.lanes = mergeLanes(alternate.lanes, renderLanes); - } +var SUSPENDED_MARKER = { + dehydrated: null, + retryLane: NoLane +}; - scheduleWorkOnParentPath(fiber.return, renderLanes); +function mountSuspenseOffscreenState(renderLanes) { + return { + baseLanes: renderLanes, + cachePool: getSuspendedCachePool() + }; } -function propagateSuspenseContextChange( +function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { + var cachePool = null; + + return { + baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), + cachePool: cachePool + }; +} // TODO: Probably should inline this back + +function shouldRemainOnFallback( + suspenseContext, + current, workInProgress, - firstChild, renderLanes ) { - // Mark any Suspense boundaries with fallbacks as having work to do. - // If they were previously forced into fallbacks, they may now be able - // to unblock. - var node = firstChild; - - while (node !== null) { - if (node.tag === SuspenseComponent) { - var state = node.memoizedState; - - if (state !== null) { - scheduleWorkOnFiber(node, renderLanes); - } - } else if (node.tag === SuspenseListComponent) { - // If the tail is hidden there might not be an Suspense boundaries - // to schedule work on. In this case we have to schedule it on the - // list itself. - // We don't have to traverse to the children of the list since - // the list will propagate the change when it rerenders. - scheduleWorkOnFiber(node, renderLanes); - } else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } - - if (node === workInProgress) { - return; - } - - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; - } + // If we're already showing a fallback, there are cases where we need to + // remain on that fallback regardless of whether the content has resolved. + // For example, SuspenseList coordinates when nested content appears. + if (current !== null) { + var suspenseState = current.memoizedState; - node = node.return; + if (suspenseState === null) { + // Currently showing content. Don't hide it, even if ForceSuspenseFallack + // is true. More precise name might be "ForceRemainSuspenseFallback". + // Note: This is a factoring smell. Can't remain on a fallback if there's + // no fallback to remain on. + return false; } + } // Not currently showing content. Consult the Suspense context. - node.sibling.return = node.return; - node = node.sibling; - } + return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); } -function findLastContentRow(firstChild) { - // This is going to find the last row among these children that is already - // showing content on the screen, as opposed to being in fallback state or - // new. If a row has multiple Suspense boundaries, any of them being in the - // fallback state, counts as the whole row being in a fallback state. - // Note that the "rows" will be workInProgress, but any nested children - // will still be current since we haven't rendered them yet. The mounted - // order may not be the same as the new order. We use the new order. - var row = firstChild; - var lastContentRow = null; +function getRemainingWorkInPrimaryTree(current, renderLanes) { + // TODO: Should not remove render lanes that were pinged during this render + return removeLanes(current.childLanes, renderLanes); +} - while (row !== null) { - var currentRow = row.alternate; // New rows can't be content rows. +function updateSuspenseComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. - if (currentRow !== null && findFirstSuspended(currentRow) === null) { - lastContentRow = row; + { + if (shouldSuspend(workInProgress)) { + workInProgress.flags |= DidCapture; } - - row = row.sibling; } - return lastContentRow; -} + var suspenseContext = suspenseStackCursor.current; + var showFallback = false; + var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; -function validateRevealOrder(revealOrder) { - { - if ( - revealOrder !== undefined && - revealOrder !== "forwards" && - revealOrder !== "backwards" && - revealOrder !== "together" && - !didWarnAboutRevealOrder[revealOrder] - ) { - didWarnAboutRevealOrder[revealOrder] = true; + if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { + // Something in this boundary's subtree already suspended. Switch to + // rendering the fallback children. + showFallback = true; + workInProgress.flags &= ~DidCapture; + } else { + // Attempting the main content + if (current === null || current.memoizedState !== null) { + // This is a new mount or this boundary is already showing a fallback state. + // Mark this subtree context as having at least one invisible parent that could + // handle the fallback state. + // Boundaries without fallbacks or should be avoided are not considered since + // they cannot handle preferred fallback states. + if ( + nextProps.fallback !== undefined && + nextProps.unstable_avoidThisFallback !== true + ) { + suspenseContext = addSubtreeSuspenseContext( + suspenseContext, + InvisibleParentSuspenseContext + ); + } + } + } - if (typeof revealOrder === "string") { - switch (revealOrder.toLowerCase()) { - case "together": - case "forwards": - case "backwards": { - error( - '"%s" is not a valid value for revealOrder on . ' + - 'Use lowercase "%s" instead.', - revealOrder, - revealOrder.toLowerCase() - ); + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense + // boundary's children. This involves some custom reconcilation logic. Two + // main reasons this is so complicated. + // + // First, Legacy Mode has different semantics for backwards compatibility. The + // primary tree will commit in an inconsistent state, so when we do the + // second pass to render the fallback, we do some exceedingly, uh, clever + // hacks to make that not totally break. Like transferring effects and + // deletions from hidden tree. In Concurrent Mode, it's much simpler, + // because we bailout on the primary tree completely and leave it in its old + // state, no effects. Same as what we do for Offscreen (except that + // Offscreen doesn't have the first render pass). + // + // Second is hydration. During hydration, the Suspense fiber has a slightly + // different layout, where the child points to a dehydrated fragment, which + // contains the DOM rendered by the server. + // + // Third, even if you set all that aside, Suspense is like error boundaries in + // that we first we try to render one tree, and if that fails, we render again + // and switch to a different tree. Like a try/catch block. So we have to track + // which branch we're currently rendering. Ideally we would model this using + // a stack. - break; - } + if (current === null) { + // Initial mount + // If we're currently hydrating, try to hydrate this boundary. + // But only if this has a fallback. + if (nextProps.fallback !== undefined); - case "forward": - case "backward": { - error( - '"%s" is not a valid value for revealOrder on . ' + - 'React uses the -s suffix in the spelling. Use "%ss" instead.', - revealOrder, - revealOrder.toLowerCase() - ); + var nextPrimaryChildren = nextProps.children; + var nextFallbackChildren = nextProps.fallback; - break; - } + if (showFallback) { + var fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); + var primaryChildFragment = workInProgress.child; + primaryChildFragment.memoizedState = mountSuspenseOffscreenState( + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return fallbackFragment; + } else if (typeof nextProps.unstable_expectedLoadTime === "number") { + // This is a CPU-bound tree. Skip this tree and show a placeholder to + // unblock the surrounding content. Then immediately retry after the + // initial commit. + var _fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); - default: - error( - '"%s" is not a supported revealOrder on . ' + - 'Did you mean "together", "forwards" or "backwards"?', - revealOrder - ); + var _primaryChildFragment = workInProgress.child; + _primaryChildFragment.memoizedState = mountSuspenseOffscreenState( + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; // Since nothing actually suspended, there will nothing to ping this to + // get it started back up to attempt the next item. While in terms of + // priority this work has the same priority as this current render, it's + // not part of the same transition once the transition has committed. If + // it's sync, we still want to yield so that it can be painted. + // Conceptually, this is really the same as pinging. We can use any + // RetryLane even if it's the one currently rendering since we're leaving + // it behind on this node. - break; - } - } else { - error( - "%s is not a supported value for revealOrder on . " + - 'Did you mean "together", "forwards" or "backwards"?', - revealOrder - ); - } + workInProgress.lanes = SomeRetryLane; + return _fallbackFragment; + } else { + return mountSuspensePrimaryChildren( + workInProgress, + nextPrimaryChildren, + renderLanes + ); } - } -} + } else { + // This is an update. + // If the current fiber has a SuspenseState, that means it's already showing + // a fallback. + var prevState = current.memoizedState; -function validateTailOptions(tailMode, revealOrder) { - { - if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { - if (tailMode !== "collapsed" && tailMode !== "hidden") { - didWarnAboutTailOptions[tailMode] = true; + if (prevState !== null) { + if (showFallback) { + var _nextFallbackChildren2 = nextProps.fallback; + var _nextPrimaryChildren2 = nextProps.children; - error( - '"%s" is not a supported value for tail on . ' + - 'Did you mean "collapsed" or "hidden"?', - tailMode + var _fallbackChildFragment = updateSuspenseFallbackChildren( + current, + workInProgress, + _nextPrimaryChildren2, + _nextFallbackChildren2, + renderLanes ); - } else if (revealOrder !== "forwards" && revealOrder !== "backwards") { - didWarnAboutTailOptions[tailMode] = true; - error( - ' is only valid if revealOrder is ' + - '"forwards" or "backwards". ' + - 'Did you mean to specify revealOrder="forwards"?', - tailMode + var _primaryChildFragment3 = workInProgress.child; + var prevOffscreenState = current.child.memoizedState; + _primaryChildFragment3.memoizedState = + prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); + _primaryChildFragment3.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return _fallbackChildFragment; + } else { + var _nextPrimaryChildren3 = nextProps.children; + + var _primaryChildFragment4 = updateSuspensePrimaryChildren( + current, + workInProgress, + _nextPrimaryChildren3, + renderLanes ); + + workInProgress.memoizedState = null; + return _primaryChildFragment4; } - } - } -} + } else { + // The current tree is not already showing a fallback. + if (showFallback) { + // Timed out. + var _nextFallbackChildren3 = nextProps.fallback; + var _nextPrimaryChildren4 = nextProps.children; -function validateSuspenseListNestedChild(childSlot, index) { - { - var isAnArray = isArray(childSlot); - var isIterable = - !isAnArray && typeof getIteratorFn(childSlot) === "function"; + var _fallbackChildFragment2 = updateSuspenseFallbackChildren( + current, + workInProgress, + _nextPrimaryChildren4, + _nextFallbackChildren3, + renderLanes + ); - if (isAnArray || isIterable) { - var type = isAnArray ? "array" : "iterable"; + var _primaryChildFragment5 = workInProgress.child; + var _prevOffscreenState = current.child.memoizedState; + _primaryChildFragment5.memoizedState = + _prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(_prevOffscreenState, renderLanes); + _primaryChildFragment5.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes + ); // Skip the primary children, and continue working on the + // fallback children. - error( - "A nested %s was passed to row #%s in . Wrap it in " + - "an additional SuspenseList to configure its revealOrder: " + - " ... " + - "{%s} ... " + - "", - type, - index, - type - ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return _fallbackChildFragment2; + } else { + // Still haven't timed out. Continue rendering the children, like we + // normally do. + var _nextPrimaryChildren5 = nextProps.children; - return false; + var _primaryChildFragment6 = updateSuspensePrimaryChildren( + current, + workInProgress, + _nextPrimaryChildren5, + renderLanes + ); + + workInProgress.memoizedState = null; + return _primaryChildFragment6; + } } } - - return true; } -function validateSuspenseListChildren(children, revealOrder) { - { - if ( - (revealOrder === "forwards" || revealOrder === "backwards") && - children !== undefined && - children !== null && - children !== false - ) { - if (isArray(children)) { - for (var i = 0; i < children.length; i++) { - if (!validateSuspenseListNestedChild(children[i], i)) { - return; - } - } - } else { - var iteratorFn = getIteratorFn(children); - - if (typeof iteratorFn === "function") { - var childrenIterator = iteratorFn.call(children); +function mountSuspensePrimaryChildren( + workInProgress, + primaryChildren, + renderLanes +) { + var mode = workInProgress.mode; + var primaryChildProps = { + mode: "visible", + children: primaryChildren + }; + var primaryChildFragment = createFiberFromOffscreen( + primaryChildProps, + mode, + renderLanes, + null + ); + primaryChildFragment.return = workInProgress; + workInProgress.child = primaryChildFragment; + return primaryChildFragment; +} - if (childrenIterator) { - var step = childrenIterator.next(); - var _i = 0; +function mountSuspenseFallbackChildren( + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + var mode = workInProgress.mode; + var progressedPrimaryFragment = workInProgress.child; + var primaryChildProps = { + mode: "hidden", + children: primaryChildren + }; + var primaryChildFragment; + var fallbackChildFragment; - for (; !step.done; step = childrenIterator.next()) { - if (!validateSuspenseListNestedChild(step.value, _i)) { - return; - } + if ( + (mode & ConcurrentMode) === NoMode && + progressedPrimaryFragment !== null + ) { + // In legacy mode, we commit the primary tree as if it successfully + // completed, even though it's in an inconsistent state. + primaryChildFragment = progressedPrimaryFragment; + primaryChildFragment.childLanes = NoLanes; + primaryChildFragment.pendingProps = primaryChildProps; - _i++; - } - } - } else { - error( - 'A single row was passed to a . ' + - "This is not useful since it needs multiple rows. " + - "Did you mean to pass multiple children or an array?", - revealOrder - ); - } - } + if (workInProgress.mode & ProfileMode) { + // Reset the durations from the first pass so they aren't included in the + // final amounts. This seems counterintuitive, since we're intentionally + // not measuring part of the render phase, but this makes it match what we + // do in Concurrent Mode. + primaryChildFragment.actualDuration = 0; + primaryChildFragment.actualStartTime = -1; + primaryChildFragment.selfBaseDuration = 0; + primaryChildFragment.treeBaseDuration = 0; } + + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); + } else { + primaryChildFragment = createFiberFromOffscreen( + primaryChildProps, + mode, + NoLanes, + null + ); + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); } + + primaryChildFragment.return = workInProgress; + fallbackChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; } -function initSuspenseListRenderState( +function createWorkInProgressOffscreenFiber(current, offscreenProps) { + // The props argument to `createWorkInProgress` is `any` typed, so we use this + // wrapper function to constrain it. + return createWorkInProgress(current, offscreenProps); +} + +function updateSuspensePrimaryChildren( + current, workInProgress, - isBackwards, - tail, - lastContentRow, - tailMode + primaryChildren, + renderLanes ) { - var renderState = workInProgress.memoizedState; + var currentPrimaryChildFragment = current.child; + var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; + var primaryChildFragment = createWorkInProgressOffscreenFiber( + currentPrimaryChildFragment, + { + mode: "visible", + children: primaryChildren + } + ); - if (renderState === null) { - workInProgress.memoizedState = { - isBackwards: isBackwards, - rendering: null, - renderingStartTime: 0, - last: lastContentRow, - tail: tail, - tailMode: tailMode - }; - } else { - // We can reuse the existing object from previous renders. - renderState.isBackwards = isBackwards; - renderState.rendering = null; - renderState.renderingStartTime = 0; - renderState.last = lastContentRow; - renderState.tail = tail; - renderState.tailMode = tailMode; + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + primaryChildFragment.lanes = renderLanes; } -} // This can end up rendering this component multiple passes. -// The first pass splits the children fibers into two sets. A head and tail. -// We first render the head. If anything is in fallback state, we do another -// pass through beginWork to rerender all children (including the tail) with -// the force suspend context. If the first render didn't have anything in -// in fallback state. Then we render each row in the tail one-by-one. -// That happens in the completeWork phase without going back to beginWork. -function updateSuspenseListComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; - var revealOrder = nextProps.revealOrder; - var tailMode = nextProps.tail; - var newChildren = nextProps.children; - validateRevealOrder(revealOrder); - validateTailOptions(tailMode, revealOrder); - validateSuspenseListChildren(newChildren, revealOrder); - reconcileChildren(current, workInProgress, newChildren, renderLanes); - var suspenseContext = suspenseStackCursor.current; - var shouldForceFallback = hasSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); + primaryChildFragment.return = workInProgress; + primaryChildFragment.sibling = null; - if (shouldForceFallback) { - suspenseContext = setShallowSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); - workInProgress.flags |= DidCapture; - } else { - var didSuspendBefore = - current !== null && (current.flags & DidCapture) !== NoFlags; + if (currentFallbackChildFragment !== null) { + // Delete the fallback child fragment + var deletions = workInProgress.deletions; - if (didSuspendBefore) { - // If we previously forced a fallback, we need to schedule work - // on any nested boundaries to let them know to try to render - // again. This is the same as context updating. - propagateSuspenseContextChange( - workInProgress, - workInProgress.child, - renderLanes - ); + if (deletions === null) { + workInProgress.deletions = [currentFallbackChildFragment]; + workInProgress.flags |= ChildDeletion; + } else { + deletions.push(currentFallbackChildFragment); } - - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); } - pushSuspenseContext(workInProgress, suspenseContext); - - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - // In legacy mode, SuspenseList doesn't work so we just - // use make it a noop by treating it as the default revealOrder. - workInProgress.memoizedState = null; - } else { - switch (revealOrder) { - case "forwards": { - var lastContentRow = findLastContentRow(workInProgress.child); - var tail; - - if (lastContentRow === null) { - // The whole list is part of the tail. - // TODO: We could fast path by just rendering the tail now. - tail = workInProgress.child; - workInProgress.child = null; - } else { - // Disconnect the tail rows after the content row. - // We're going to render them separately later. - tail = lastContentRow.sibling; - lastContentRow.sibling = null; - } + workInProgress.child = primaryChildFragment; + return primaryChildFragment; +} - initSuspenseListRenderState( - workInProgress, - false, // isBackwards - tail, - lastContentRow, - tailMode - ); - break; - } +function updateSuspenseFallbackChildren( + current, + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + var mode = workInProgress.mode; + var currentPrimaryChildFragment = current.child; + var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; + var primaryChildProps = { + mode: "hidden", + children: primaryChildren + }; + var primaryChildFragment; - case "backwards": { - // We're going to find the first row that has existing content. - // At the same time we're going to reverse the list of everything - // we pass in the meantime. That's going to be our tail in reverse - // order. - var _tail = null; - var row = workInProgress.child; - workInProgress.child = null; + if ( + // In legacy mode, we commit the primary tree as if it successfully + // completed, even though it's in an inconsistent state. + (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was + // already cloned. In legacy mode, the only case where this isn't true is + // when DevTools forces us to display a fallback; we skip the first render + // pass entirely and go straight to rendering the fallback. (In Concurrent + // Mode, SuspenseList can also trigger this scenario, but this is a legacy- + // only codepath.) + workInProgress.child !== currentPrimaryChildFragment + ) { + var progressedPrimaryFragment = workInProgress.child; + primaryChildFragment = progressedPrimaryFragment; + primaryChildFragment.childLanes = NoLanes; + primaryChildFragment.pendingProps = primaryChildProps; - while (row !== null) { - var currentRow = row.alternate; // New rows can't be content rows. + if (workInProgress.mode & ProfileMode) { + // Reset the durations from the first pass so they aren't included in the + // final amounts. This seems counterintuitive, since we're intentionally + // not measuring part of the render phase, but this makes it match what we + // do in Concurrent Mode. + primaryChildFragment.actualDuration = 0; + primaryChildFragment.actualStartTime = -1; + primaryChildFragment.selfBaseDuration = + currentPrimaryChildFragment.selfBaseDuration; + primaryChildFragment.treeBaseDuration = + currentPrimaryChildFragment.treeBaseDuration; + } // The fallback fiber was added as a deletion during the first pass. + // However, since we're going to remain on the fallback, we no longer want + // to delete it. - if (currentRow !== null && findFirstSuspended(currentRow) === null) { - // This is the beginning of the main content. - workInProgress.child = row; - break; - } + workInProgress.deletions = null; + } else { + primaryChildFragment = createWorkInProgressOffscreenFiber( + currentPrimaryChildFragment, + primaryChildProps + ); // Since we're reusing a current tree, we need to reuse the flags, too. + // (We don't do this in legacy mode, because in legacy mode we don't re-use + // the current tree; see previous branch.) - var nextRow = row.sibling; - row.sibling = _tail; - _tail = row; - row = nextRow; - } // TODO: If workInProgress.child is null, we can continue on the tail immediately. + primaryChildFragment.subtreeFlags = + currentPrimaryChildFragment.subtreeFlags & StaticMask; + } - initSuspenseListRenderState( - workInProgress, - true, // isBackwards - _tail, - null, // last - tailMode - ); - break; - } + var fallbackChildFragment; - case "together": { - initSuspenseListRenderState( - workInProgress, - false, // isBackwards - null, // tail - null, // last - undefined - ); - break; - } + if (currentFallbackChildFragment !== null) { + fallbackChildFragment = createWorkInProgress( + currentFallbackChildFragment, + fallbackChildren + ); + } else { + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); // Needs a placement effect because the parent (the Suspense boundary) already + // mounted but this is a new fiber. - default: { - // The default reveal order is the same as not having - // a boundary. - workInProgress.memoizedState = null; - } - } + fallbackChildFragment.flags |= Placement; } - return workInProgress.child; + fallbackChildFragment.return = workInProgress; + primaryChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; } -function updatePortalComponent(current, workInProgress, renderLanes) { - pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); - var nextChildren = workInProgress.pendingProps; +function scheduleWorkOnFiber(fiber, renderLanes) { + fiber.lanes = mergeLanes(fiber.lanes, renderLanes); + var alternate = fiber.alternate; - if (current === null) { - // Portals are special because we don't append the children during mount - // but at commit. Therefore we need to track insertions which the normal - // flow doesn't do during mount. This doesn't happen at the root because - // the root always starts with a "current" with a null child. - // TODO: Consider unifying this with how the root works. - workInProgress.child = reconcileChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - } else { - reconcileChildren(current, workInProgress, nextChildren, renderLanes); + if (alternate !== null) { + alternate.lanes = mergeLanes(alternate.lanes, renderLanes); } - return workInProgress.child; + scheduleWorkOnParentPath(fiber.return, renderLanes); } -var hasWarnedAboutUsingNoValuePropOnContextProvider = false; - -function updateContextProvider(current, workInProgress, renderLanes) { - var providerType = workInProgress.type; - var context = providerType._context; - var newProps = workInProgress.pendingProps; - var oldProps = workInProgress.memoizedProps; - var newValue = newProps.value; +function propagateSuspenseContextChange( + workInProgress, + firstChild, + renderLanes +) { + // Mark any Suspense boundaries with fallbacks as having work to do. + // If they were previously forced into fallbacks, they may now be able + // to unblock. + var node = firstChild; - { - if (!("value" in newProps)) { - if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { - hasWarnedAboutUsingNoValuePropOnContextProvider = true; + while (node !== null) { + if (node.tag === SuspenseComponent) { + var state = node.memoizedState; - error( - "The `value` prop is required for the ``. Did you misspell it or forget to pass it?" - ); + if (state !== null) { + scheduleWorkOnFiber(node, renderLanes); } + } else if (node.tag === SuspenseListComponent) { + // If the tail is hidden there might not be an Suspense boundaries + // to schedule work on. In this case we have to schedule it on the + // list itself. + // We don't have to traverse to the children of the list since + // the list will propagate the change when it rerenders. + scheduleWorkOnFiber(node, renderLanes); + } else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; } - var providerPropTypes = workInProgress.type.propTypes; + if (node === workInProgress) { + return; + } - if (providerPropTypes) { - checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider"); + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; + } + + node = node.return; } + + node.sibling.return = node.return; + node = node.sibling; } +} - pushProvider(workInProgress, context, newValue); +function findLastContentRow(firstChild) { + // This is going to find the last row among these children that is already + // showing content on the screen, as opposed to being in fallback state or + // new. If a row has multiple Suspense boundaries, any of them being in the + // fallback state, counts as the whole row being in a fallback state. + // Note that the "rows" will be workInProgress, but any nested children + // will still be current since we haven't rendered them yet. The mounted + // order may not be the same as the new order. We use the new order. + var row = firstChild; + var lastContentRow = null; - { - if (oldProps !== null) { - var oldValue = oldProps.value; + while (row !== null) { + var currentRow = row.alternate; // New rows can't be content rows. - if (objectIs(oldValue, newValue)) { - // No change. Bailout early if children are the same. - if (oldProps.children === newProps.children && !hasContextChanged()) { - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); - } - } else { - // The context value changed. Search for matching consumers and schedule - // them to update. - propagateContextChange(workInProgress, context, renderLanes); - } + if (currentRow !== null && findFirstSuspended(currentRow) === null) { + lastContentRow = row; } + + row = row.sibling; } - var newChildren = newProps.children; - reconcileChildren(current, workInProgress, newChildren, renderLanes); - return workInProgress.child; + return lastContentRow; } -var hasWarnedAboutUsingContextAsConsumer = false; +function validateRevealOrder(revealOrder) { + { + if ( + revealOrder !== undefined && + revealOrder !== "forwards" && + revealOrder !== "backwards" && + revealOrder !== "together" && + !didWarnAboutRevealOrder[revealOrder] + ) { + didWarnAboutRevealOrder[revealOrder] = true; -function updateContextConsumer(current, workInProgress, renderLanes) { - var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In - // DEV mode, we create a separate object for Context.Consumer that acts - // like a proxy to Context. This proxy object adds unnecessary code in PROD - // so we use the old behaviour (Context.Consumer references Context) to - // reduce size and overhead. The separate object references context via - // a property called "_context", which also gives us the ability to check - // in DEV mode if this property exists or not and warn if it does not. + if (typeof revealOrder === "string") { + switch (revealOrder.toLowerCase()) { + case "together": + case "forwards": + case "backwards": { + error( + '"%s" is not a valid value for revealOrder on . ' + + 'Use lowercase "%s" instead.', + revealOrder, + revealOrder.toLowerCase() + ); - { - if (context._context === undefined) { - // This may be because it's a Context (rather than a Consumer). - // Or it may be because it's older React where they're the same thing. - // We only want to warn if we're sure it's a new React. - if (context !== context.Consumer) { - if (!hasWarnedAboutUsingContextAsConsumer) { - hasWarnedAboutUsingContextAsConsumer = true; + break; + } - error( - "Rendering directly is not supported and will be removed in " + - "a future major release. Did you mean to render instead?" - ); - } - } - } else { - context = context._context; - } - } + case "forward": + case "backward": { + error( + '"%s" is not a valid value for revealOrder on . ' + + 'React uses the -s suffix in the spelling. Use "%ss" instead.', + revealOrder, + revealOrder.toLowerCase() + ); - var newProps = workInProgress.pendingProps; - var render = newProps.children; + break; + } - { - if (typeof render !== "function") { - error( - "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." - ); + default: + error( + '"%s" is not a supported revealOrder on . ' + + 'Did you mean "together", "forwards" or "backwards"?', + revealOrder + ); + + break; + } + } else { + error( + "%s is not a supported value for revealOrder on . " + + 'Did you mean "together", "forwards" or "backwards"?', + revealOrder + ); + } } } +} - prepareToReadContext(workInProgress, renderLanes); - var newValue = readContext(context); - var newChildren; - +function validateTailOptions(tailMode, revealOrder) { { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - newChildren = render(newValue); - setIsRendering(false); - } // React DevTools reads this flag. - - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, newChildren, renderLanes); - return workInProgress.child; -} + if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { + if (tailMode !== "collapsed" && tailMode !== "hidden") { + didWarnAboutTailOptions[tailMode] = true; -function markWorkInProgressReceivedUpdate() { - didReceiveUpdate = true; -} + error( + '"%s" is not a supported value for tail on . ' + + 'Did you mean "collapsed" or "hidden"?', + tailMode + ); + } else if (revealOrder !== "forwards" && revealOrder !== "backwards") { + didWarnAboutTailOptions[tailMode] = true; -function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { - if (current !== null) { - // Reuse previous dependencies - workInProgress.dependencies = current.dependencies; + error( + ' is only valid if revealOrder is ' + + '"forwards" or "backwards". ' + + 'Did you mean to specify revealOrder="forwards"?', + tailMode + ); + } + } } +} +function validateSuspenseListNestedChild(childSlot, index) { { - // Don't update "base" render times for bailouts. - stopProfilerTimerIfRunning(); - } + var isAnArray = isArray(childSlot); + var isIterable = + !isAnArray && typeof getIteratorFn(childSlot) === "function"; - markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. + if (isAnArray || isIterable) { + var type = isAnArray ? "array" : "iterable"; - if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { - // The children don't have any work either. We can skip them. - // TODO: Once we add back resuming, we should check if the children are - // a work-in-progress set. If so, we need to transfer their effects. - { - return null; + error( + "A nested %s was passed to row #%s in . Wrap it in " + + "an additional SuspenseList to configure its revealOrder: " + + " ... " + + "{%s} ... " + + "", + type, + index, + type + ); + + return false; } - } // This fiber doesn't have work, but its subtree does. Clone the child - // fibers and continue. + } - cloneChildFibers(current, workInProgress); - return workInProgress.child; + return true; } -function remountFiber(current, oldWorkInProgress, newWorkInProgress) { +function validateSuspenseListChildren(children, revealOrder) { { - var returnFiber = oldWorkInProgress.return; - - if (returnFiber === null) { - throw new Error("Cannot swap the root fiber."); - } // Disconnect from the old current. - // It will get deleted. - - current.alternate = null; - oldWorkInProgress.alternate = null; // Connect to the new tree. - - newWorkInProgress.index = oldWorkInProgress.index; - newWorkInProgress.sibling = oldWorkInProgress.sibling; - newWorkInProgress.return = oldWorkInProgress.return; - newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. + if ( + (revealOrder === "forwards" || revealOrder === "backwards") && + children !== undefined && + children !== null && + children !== false + ) { + if (isArray(children)) { + for (var i = 0; i < children.length; i++) { + if (!validateSuspenseListNestedChild(children[i], i)) { + return; + } + } + } else { + var iteratorFn = getIteratorFn(children); - if (oldWorkInProgress === returnFiber.child) { - returnFiber.child = newWorkInProgress; - } else { - var prevSibling = returnFiber.child; + if (typeof iteratorFn === "function") { + var childrenIterator = iteratorFn.call(children); - if (prevSibling === null) { - throw new Error("Expected parent to have a child."); - } + if (childrenIterator) { + var step = childrenIterator.next(); + var _i = 0; - while (prevSibling.sibling !== oldWorkInProgress) { - prevSibling = prevSibling.sibling; + for (; !step.done; step = childrenIterator.next()) { + if (!validateSuspenseListNestedChild(step.value, _i)) { + return; + } - if (prevSibling === null) { - throw new Error("Expected to find the previous sibling."); + _i++; + } + } + } else { + error( + 'A single row was passed to a . ' + + "This is not useful since it needs multiple rows. " + + "Did you mean to pass multiple children or an array?", + revealOrder + ); } } - - prevSibling.sibling = newWorkInProgress; - } // Delete the old fiber and place the new one. - // Since the old fiber is disconnected, we have to schedule it manually. - - var deletions = returnFiber.deletions; - - if (deletions === null) { - returnFiber.deletions = [current]; - returnFiber.flags |= ChildDeletion; - } else { - deletions.push(current); } - - newWorkInProgress.flags |= Placement; // Restart work from the new fiber. - - return newWorkInProgress; } } -function beginWork(current, workInProgress, renderLanes) { - var updateLanes = workInProgress.lanes; +function initSuspenseListRenderState( + workInProgress, + isBackwards, + tail, + lastContentRow, + tailMode +) { + var renderState = workInProgress.memoizedState; - { - if (workInProgress._debugNeedsRemount && current !== null) { - // This will restart the begin phase with a new fiber. - return remountFiber( - current, - workInProgress, - createFiberFromTypeAndProps( - workInProgress.type, - workInProgress.key, - workInProgress.pendingProps, - workInProgress._debugOwner || null, - workInProgress.mode, - workInProgress.lanes - ) - ); - } + if (renderState === null) { + workInProgress.memoizedState = { + isBackwards: isBackwards, + rendering: null, + renderingStartTime: 0, + last: lastContentRow, + tail: tail, + tailMode: tailMode + }; + } else { + // We can reuse the existing object from previous renders. + renderState.isBackwards = isBackwards; + renderState.rendering = null; + renderState.renderingStartTime = 0; + renderState.last = lastContentRow; + renderState.tail = tail; + renderState.tailMode = tailMode; } +} // This can end up rendering this component multiple passes. +// The first pass splits the children fibers into two sets. A head and tail. +// We first render the head. If anything is in fallback state, we do another +// pass through beginWork to rerender all children (including the tail) with +// the force suspend context. If the first render didn't have anything in +// in fallback state. Then we render each row in the tail one-by-one. +// That happens in the completeWork phase without going back to beginWork. - if (current !== null) { - var oldProps = current.memoizedProps; - var newProps = workInProgress.pendingProps; - - if ( - oldProps !== newProps || - hasContextChanged() || // Force a re-render if the implementation changed due to hot reload: - workInProgress.type !== current.type - ) { - // If props or context changed, mark the fiber as having performed work. - // This may be unset if the props are determined to be equal later (memo). - didReceiveUpdate = true; - } else if (!includesSomeLane(renderLanes, updateLanes)) { - didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering - // the begin phase. There's still some bookkeeping we that needs to be done - // in this optimized path, mostly pushing stuff onto the stack. - - switch (workInProgress.tag) { - case HostRoot: - pushHostRootContext(workInProgress); - break; +function updateSuspenseListComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; + var revealOrder = nextProps.revealOrder; + var tailMode = nextProps.tail; + var newChildren = nextProps.children; + validateRevealOrder(revealOrder); + validateTailOptions(tailMode, revealOrder); + validateSuspenseListChildren(newChildren, revealOrder); + reconcileChildren(current, workInProgress, newChildren, renderLanes); + var suspenseContext = suspenseStackCursor.current; + var shouldForceFallback = hasSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); - case HostComponent: - pushHostContext(workInProgress); - break; + if (shouldForceFallback) { + suspenseContext = setShallowSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + workInProgress.flags |= DidCapture; + } else { + var didSuspendBefore = + current !== null && (current.flags & DidCapture) !== NoFlags; - case ClassComponent: { - var Component = workInProgress.type; + if (didSuspendBefore) { + // If we previously forced a fallback, we need to schedule work + // on any nested boundaries to let them know to try to render + // again. This is the same as context updating. + propagateSuspenseContextChange( + workInProgress, + workInProgress.child, + renderLanes + ); + } - if (isContextProvider(Component)) { - pushContextProvider(workInProgress); - } + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + } - break; - } + pushSuspenseContext(workInProgress, suspenseContext); - case HostPortal: - pushHostContainer( - workInProgress, - workInProgress.stateNode.containerInfo - ); - break; + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + // In legacy mode, SuspenseList doesn't work so we just + // use make it a noop by treating it as the default revealOrder. + workInProgress.memoizedState = null; + } else { + switch (revealOrder) { + case "forwards": { + var lastContentRow = findLastContentRow(workInProgress.child); + var tail; - case ContextProvider: { - var newValue = workInProgress.memoizedProps.value; - var context = workInProgress.type._context; - pushProvider(workInProgress, context, newValue); - break; + if (lastContentRow === null) { + // The whole list is part of the tail. + // TODO: We could fast path by just rendering the tail now. + tail = workInProgress.child; + workInProgress.child = null; + } else { + // Disconnect the tail rows after the content row. + // We're going to render them separately later. + tail = lastContentRow.sibling; + lastContentRow.sibling = null; } - case Profiler: - { - // Profiler should only call onRender when one of its descendants actually rendered. - var hasChildWork = includesSomeLane( - renderLanes, - workInProgress.childLanes - ); + initSuspenseListRenderState( + workInProgress, + false, // isBackwards + tail, + lastContentRow, + tailMode + ); + break; + } - if (hasChildWork) { - workInProgress.flags |= Update; - } + case "backwards": { + // We're going to find the first row that has existing content. + // At the same time we're going to reverse the list of everything + // we pass in the meantime. That's going to be our tail in reverse + // order. + var _tail = null; + var row = workInProgress.child; + workInProgress.child = null; + + while (row !== null) { + var currentRow = row.alternate; // New rows can't be content rows. + + if (currentRow !== null && findFirstSuspended(currentRow) === null) { + // This is the beginning of the main content. + workInProgress.child = row; + break; } - break; + var nextRow = row.sibling; + row.sibling = _tail; + _tail = row; + row = nextRow; + } // TODO: If workInProgress.child is null, we can continue on the tail immediately. - case SuspenseComponent: { - var state = workInProgress.memoizedState; + initSuspenseListRenderState( + workInProgress, + true, // isBackwards + _tail, + null, // last + tailMode + ); + break; + } - if (state !== null) { - // whether to retry the primary children, or to skip over it and - // go straight to the fallback. Check the priority of the primary - // child fragment. + case "together": { + initSuspenseListRenderState( + workInProgress, + false, // isBackwards + null, // tail + null, // last + undefined + ); + break; + } - var primaryChildFragment = workInProgress.child; - var primaryChildLanes = primaryChildFragment.childLanes; + default: { + // The default reveal order is the same as not having + // a boundary. + workInProgress.memoizedState = null; + } + } + } - if (includesSomeLane(renderLanes, primaryChildLanes)) { - // The primary children have pending work. Use the normal path - // to attempt to render the primary children again. - return updateSuspenseComponent( - current, - workInProgress, - renderLanes - ); - } else { - // The primary child fragment does not have pending work marked - // on it - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ); // The primary children do not have pending work with sufficient - // priority. Bailout. + return workInProgress.child; +} - var child = bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); +function updatePortalComponent(current, workInProgress, renderLanes) { + pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); + var nextChildren = workInProgress.pendingProps; - if (child !== null) { - // The fallback children have pending work. Skip over the - // primary children and work on the fallback. - return child.sibling; - } else { - // Note: We can return `null` here because we already checked - // whether there were nested context consumers, via the call to - // `bailoutOnAlreadyFinishedWork` above. - return null; - } - } - } else { - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ); - } + if (current === null) { + // Portals are special because we don't append the children during mount + // but at commit. Therefore we need to track insertions which the normal + // flow doesn't do during mount. This doesn't happen at the root because + // the root always starts with a "current" with a null child. + // TODO: Consider unifying this with how the root works. + workInProgress.child = reconcileChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); + } else { + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } - break; - } + return workInProgress.child; +} - case SuspenseListComponent: { - var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; +var hasWarnedAboutUsingNoValuePropOnContextProvider = false; - var _hasChildWork = includesSomeLane( - renderLanes, - workInProgress.childLanes - ); +function updateContextProvider(current, workInProgress, renderLanes) { + var providerType = workInProgress.type; + var context = providerType._context; + var newProps = workInProgress.pendingProps; + var oldProps = workInProgress.memoizedProps; + var newValue = newProps.value; - if (didSuspendBefore) { - if (_hasChildWork) { - // If something was in fallback state last time, and we have all the - // same children then we're still in progressive loading state. - // Something might get unblocked by state updates or retries in the - // tree which will affect the tail. So we need to use the normal - // path to compute the correct tail. - return updateSuspenseListComponent( - current, - workInProgress, - renderLanes - ); - } // If none of the children had any work, that means that none of - // them got retried so they'll still be blocked in the same way - // as before. We can fast bail out. + { + if (!("value" in newProps)) { + if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { + hasWarnedAboutUsingNoValuePropOnContextProvider = true; - workInProgress.flags |= DidCapture; - } // If nothing suspended before and we're rendering the same children, - // then the tail doesn't matter. Anything new that suspends will work - // in the "together" mode, so we can continue from the state we had. + error( + "The `value` prop is required for the ``. Did you misspell it or forget to pass it?" + ); + } + } - var renderState = workInProgress.memoizedState; + var providerPropTypes = workInProgress.type.propTypes; - if (renderState !== null) { - // Reset to the "together" mode in case we've started a different - // update in the past but didn't complete it. - renderState.rendering = null; - renderState.tail = null; - renderState.lastEffect = null; - } + if (providerPropTypes) { + checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider"); + } + } - pushSuspenseContext(workInProgress, suspenseStackCursor.current); + pushProvider(workInProgress, context, newValue); - if (_hasChildWork) { - break; - } else { - // If none of the children had any work, that means that none of - // them got retried so they'll still be blocked in the same way - // as before. We can fast bail out. - return null; - } - } + { + if (oldProps !== null) { + var oldValue = oldProps.value; - case OffscreenComponent: - case LegacyHiddenComponent: { - // Need to check if the tree still needs to be deferred. This is - // almost identical to the logic used in the normal update path, - // so we'll just enter that. The only difference is we'll bail out - // at the next level instead of this one, because the child props - // have not changed. Which is fine. - // TODO: Probably should refactor `beginWork` to split the bailout - // path from the normal path. I'm tempted to do a labeled break here - // but I won't :) - workInProgress.lanes = NoLanes; - return updateOffscreenComponent(current, workInProgress, renderLanes); + if (objectIs(oldValue, newValue)) { + // No change. Bailout early if children are the same. + if (oldProps.children === newProps.children && !hasContextChanged()) { + return bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); } - } - - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } else { - if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { - // This is a special case that only exists for legacy mode. - // See https://github.com/facebook/react/pull/19216. - didReceiveUpdate = true; } else { - // An update was scheduled on this fiber, but there are no new props - // nor legacy context. Set this to false. If an update queue or context - // consumer produces a changed value, it will set this to true. Otherwise, - // the component will assume the children have not changed and bail out. - didReceiveUpdate = false; - } - } - } else { - didReceiveUpdate = false; - } // Before entering the begin phase, clear pending update priority. - // TODO: This assumes that we're about to evaluate the component and process - // the update queue. However, there's an exception: SimpleMemoComponent - // sometimes bails out later in the begin phase. This indicates that we should - // move this assignment out of the common path and into each branch. + // The context value changed. Search for matching consumers and schedule + // them to update. + propagateContextChange(workInProgress, context, renderLanes); + } + } + } - workInProgress.lanes = NoLanes; + var newChildren = newProps.children; + reconcileChildren(current, workInProgress, newChildren, renderLanes); + return workInProgress.child; +} - switch (workInProgress.tag) { - case IndeterminateComponent: { - return mountIndeterminateComponent( - current, - workInProgress, - workInProgress.type, - renderLanes - ); - } +var hasWarnedAboutUsingContextAsConsumer = false; - case LazyComponent: { - var elementType = workInProgress.elementType; - return mountLazyComponent( - current, - workInProgress, - elementType, - updateLanes, - renderLanes - ); - } +function updateContextConsumer(current, workInProgress, renderLanes) { + var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In + // DEV mode, we create a separate object for Context.Consumer that acts + // like a proxy to Context. This proxy object adds unnecessary code in PROD + // so we use the old behaviour (Context.Consumer references Context) to + // reduce size and overhead. The separate object references context via + // a property called "_context", which also gives us the ability to check + // in DEV mode if this property exists or not and warn if it does not. - case FunctionComponent: { - var _Component = workInProgress.type; - var unresolvedProps = workInProgress.pendingProps; - var resolvedProps = - workInProgress.elementType === _Component - ? unresolvedProps - : resolveDefaultProps(_Component, unresolvedProps); - return updateFunctionComponent( - current, - workInProgress, - _Component, - resolvedProps, - renderLanes - ); - } + { + if (context._context === undefined) { + // This may be because it's a Context (rather than a Consumer). + // Or it may be because it's older React where they're the same thing. + // We only want to warn if we're sure it's a new React. + if (context !== context.Consumer) { + if (!hasWarnedAboutUsingContextAsConsumer) { + hasWarnedAboutUsingContextAsConsumer = true; - case ClassComponent: { - var _Component2 = workInProgress.type; - var _unresolvedProps = workInProgress.pendingProps; + error( + "Rendering directly is not supported and will be removed in " + + "a future major release. Did you mean to render instead?" + ); + } + } + } else { + context = context._context; + } + } - var _resolvedProps = - workInProgress.elementType === _Component2 - ? _unresolvedProps - : resolveDefaultProps(_Component2, _unresolvedProps); + var newProps = workInProgress.pendingProps; + var render = newProps.children; - return updateClassComponent( - current, - workInProgress, - _Component2, - _resolvedProps, - renderLanes + { + if (typeof render !== "function") { + error( + "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." ); } + } - case HostRoot: - return updateHostRoot(current, workInProgress, renderLanes); + prepareToReadContext(workInProgress, renderLanes); + var newValue = readContext(context); + var newChildren; - case HostComponent: - return updateHostComponent(current, workInProgress, renderLanes); + { + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + newChildren = render(newValue); + setIsRendering(false); + } // React DevTools reads this flag. - case HostText: - return updateHostText(); + workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, newChildren, renderLanes); + return workInProgress.child; +} - case SuspenseComponent: - return updateSuspenseComponent(current, workInProgress, renderLanes); +function markWorkInProgressReceivedUpdate() { + didReceiveUpdate = true; +} - case HostPortal: - return updatePortalComponent(current, workInProgress, renderLanes); +function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { + if (current !== null) { + // Reuse previous dependencies + workInProgress.dependencies = current.dependencies; + } - case ForwardRef: { - var type = workInProgress.type; - var _unresolvedProps2 = workInProgress.pendingProps; + { + // Don't update "base" render times for bailouts. + stopProfilerTimerIfRunning(); + } - var _resolvedProps2 = - workInProgress.elementType === type - ? _unresolvedProps2 - : resolveDefaultProps(type, _unresolvedProps2); + markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. - return updateForwardRef( - current, - workInProgress, - type, - _resolvedProps2, - renderLanes - ); + if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { + // The children don't have any work either. We can skip them. + // TODO: Once we add back resuming, we should check if the children are + // a work-in-progress set. If so, we need to transfer their effects. + { + return null; } + } // This fiber doesn't have work, but its subtree does. Clone the child + // fibers and continue. - case Fragment: - return updateFragment(current, workInProgress, renderLanes); + cloneChildFibers(current, workInProgress); + return workInProgress.child; +} - case Mode: - return updateMode(current, workInProgress, renderLanes); +function remountFiber(current, oldWorkInProgress, newWorkInProgress) { + { + var returnFiber = oldWorkInProgress.return; - case Profiler: - return updateProfiler(current, workInProgress, renderLanes); + if (returnFiber === null) { + throw new Error("Cannot swap the root fiber."); + } // Disconnect from the old current. + // It will get deleted. - case ContextProvider: - return updateContextProvider(current, workInProgress, renderLanes); + current.alternate = null; + oldWorkInProgress.alternate = null; // Connect to the new tree. - case ContextConsumer: - return updateContextConsumer(current, workInProgress, renderLanes); + newWorkInProgress.index = oldWorkInProgress.index; + newWorkInProgress.sibling = oldWorkInProgress.sibling; + newWorkInProgress.return = oldWorkInProgress.return; + newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. - case MemoComponent: { - var _type2 = workInProgress.type; - var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. + if (oldWorkInProgress === returnFiber.child) { + returnFiber.child = newWorkInProgress; + } else { + var prevSibling = returnFiber.child; - var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); + if (prevSibling === null) { + throw new Error("Expected parent to have a child."); + } - { - if (workInProgress.type !== workInProgress.elementType) { - var outerPropTypes = _type2.propTypes; + while (prevSibling.sibling !== oldWorkInProgress) { + prevSibling = prevSibling.sibling; - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - _resolvedProps3, // Resolved for outer only - "prop", - getComponentNameFromType(_type2) - ); - } + if (prevSibling === null) { + throw new Error("Expected to find the previous sibling."); } } - _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); - return updateMemoComponent( - current, - workInProgress, - _type2, - _resolvedProps3, - updateLanes, - renderLanes - ); - } - - case SimpleMemoComponent: { - return updateSimpleMemoComponent( - current, - workInProgress, - workInProgress.type, - workInProgress.pendingProps, - updateLanes, - renderLanes - ); - } - - case IncompleteClassComponent: { - var _Component3 = workInProgress.type; - var _unresolvedProps4 = workInProgress.pendingProps; - - var _resolvedProps4 = - workInProgress.elementType === _Component3 - ? _unresolvedProps4 - : resolveDefaultProps(_Component3, _unresolvedProps4); - - return mountIncompleteClassComponent( - current, - workInProgress, - _Component3, - _resolvedProps4, - renderLanes - ); - } - - case SuspenseListComponent: { - return updateSuspenseListComponent(current, workInProgress, renderLanes); - } + prevSibling.sibling = newWorkInProgress; + } // Delete the old fiber and place the new one. + // Since the old fiber is disconnected, we have to schedule it manually. - case ScopeComponent: { - break; - } + var deletions = returnFiber.deletions; - case OffscreenComponent: { - return updateOffscreenComponent(current, workInProgress, renderLanes); + if (deletions === null) { + returnFiber.deletions = [current]; + returnFiber.flags |= ChildDeletion; + } else { + deletions.push(current); } - case LegacyHiddenComponent: { - return updateLegacyHiddenComponent(current, workInProgress, renderLanes); - } - } + newWorkInProgress.flags |= Placement; // Restart work from the new fiber. - { - throw Error( - "Unknown unit of work tag (" + - workInProgress.tag + - "). This error is likely caused by a bug in React. Please file an issue." - ); + return newWorkInProgress; } } -function markUpdate(workInProgress) { - // Tag the fiber with an update effect. This turns a Placement into - // a PlacementAndUpdate. - workInProgress.flags |= Update; -} - -function markRef$1(workInProgress) { - workInProgress.flags |= Ref; -} - -function hadNoMutationsEffects(current, completedWork) { - var didBailout = current !== null && current.child === completedWork.child; +function beginWork(current, workInProgress, renderLanes) { + var updateLanes = workInProgress.lanes; - if (didBailout) { - return true; + { + if (workInProgress._debugNeedsRemount && current !== null) { + // This will restart the begin phase with a new fiber. + return remountFiber( + current, + workInProgress, + createFiberFromTypeAndProps( + workInProgress.type, + workInProgress.key, + workInProgress.pendingProps, + workInProgress._debugOwner || null, + workInProgress.mode, + workInProgress.lanes + ) + ); + } } - if ((completedWork.flags & ChildDeletion) !== NoFlags) { - return false; - } // TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties` - // then we only have to check the `completedWork.subtreeFlags`. - - var child = completedWork.child; + if (current !== null) { + var oldProps = current.memoizedProps; + var newProps = workInProgress.pendingProps; - while (child !== null) { if ( - (child.flags & MutationMask) !== NoFlags || - (child.subtreeFlags & MutationMask) !== NoFlags + oldProps !== newProps || + hasContextChanged() || // Force a re-render if the implementation changed due to hot reload: + workInProgress.type !== current.type ) { - return false; - } - - child = child.sibling; - } + // If props or context changed, mark the fiber as having performed work. + // This may be unset if the props are determined to be equal later (memo). + didReceiveUpdate = true; + } else if (!includesSomeLane(renderLanes, updateLanes)) { + didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering + // the begin phase. There's still some bookkeeping we that needs to be done + // in this optimized path, mostly pushing stuff onto the stack. - return true; -} + switch (workInProgress.tag) { + case HostRoot: + pushHostRootContext(workInProgress); + break; -var appendAllChildren; -var updateHostContainer; -var updateHostComponent$1; -var updateHostText$1; + case HostComponent: + pushHostContext(workInProgress); + break; -{ - // Persistent host tree mode - appendAllChildren = function( - parent, - workInProgress, - needsVisibilityToggle, - isHidden - ) { - // We only have the top Fiber that was created but we need recurse down its - // children to find all the terminal nodes. - var node = workInProgress.child; + case ClassComponent: { + var Component = workInProgress.type; - while (node !== null) { - // eslint-disable-next-line no-labels - if (node.tag === HostComponent) { - var instance = node.stateNode; + if (isContextProvider(Component)) { + pushContextProvider(workInProgress); + } - if (needsVisibilityToggle && isHidden) { - // This child is inside a timed out tree. Hide it. - var props = node.memoizedProps; - var type = node.type; - instance = cloneHiddenInstance(instance); + break; } - appendInitialChild(parent, instance); - } else if (node.tag === HostText) { - var _instance = node.stateNode; + case HostPortal: + pushHostContainer( + workInProgress, + workInProgress.stateNode.containerInfo + ); + break; - if (needsVisibilityToggle && isHidden) { - // This child is inside a timed out tree. Hide it. - var text = node.memoizedProps; - _instance = cloneHiddenTextInstance(); + case ContextProvider: { + var newValue = workInProgress.memoizedProps.value; + var context = workInProgress.type._context; + pushProvider(workInProgress, context, newValue); + break; } - appendInitialChild(parent, _instance); - } else if (node.tag === HostPortal); - else if (node.tag === SuspenseComponent) { - if ((node.flags & Update) !== NoFlags) { - // Need to toggle the visibility of the primary children. - var newIsHidden = node.memoizedState !== null; - - if (newIsHidden) { - var primaryChildParent = node.child; - - if (primaryChildParent !== null) { - if (primaryChildParent.child !== null) { - primaryChildParent.child.return = primaryChildParent; - appendAllChildren( - parent, - primaryChildParent, - true, - newIsHidden - ); - } + case Profiler: + { + // Profiler should only call onRender when one of its descendants actually rendered. + var hasChildWork = includesSomeLane( + renderLanes, + workInProgress.childLanes + ); - var fallbackChildParent = primaryChildParent.sibling; + if (hasChildWork) { + workInProgress.flags |= Update; + } - if (fallbackChildParent !== null) { - fallbackChildParent.return = node; - node = fallbackChildParent; - continue; - } + { + // Reset effect durations for the next eventual effect phase. + // These are reset during render to allow the DevTools commit hook a chance to read them, + var stateNode = workInProgress.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; } } - } - - if (node.child !== null) { - // Continue traversing like normal - node.child.return = node; - node = node.child; - continue; - } - } else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } // $FlowFixMe This is correct but Flow is confused by the labeled break. - node = node; + break; - if (node === workInProgress) { - return; - } + case SuspenseComponent: { + var state = workInProgress.memoizedState; - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; - } + if (state !== null) { + // whether to retry the primary children, or to skip over it and + // go straight to the fallback. Check the priority of the primary + // child fragment. - node = node.return; - } + var primaryChildFragment = workInProgress.child; + var primaryChildLanes = primaryChildFragment.childLanes; - node.sibling.return = node.return; - node = node.sibling; - } - }; // An unfortunate fork of appendAllChildren because we have two different parent types. + if (includesSomeLane(renderLanes, primaryChildLanes)) { + // The primary children have pending work. Use the normal path + // to attempt to render the primary children again. + return updateSuspenseComponent( + current, + workInProgress, + renderLanes + ); + } else { + // The primary child fragment does not have pending work marked + // on it + pushSuspenseContext( + workInProgress, + setDefaultShallowSuspenseContext(suspenseStackCursor.current) + ); // The primary children do not have pending work with sufficient + // priority. Bailout. - var appendAllChildrenToContainer = function( - containerChildSet, - workInProgress, - needsVisibilityToggle, - isHidden - ) { - // We only have the top Fiber that was created but we need recurse down its - // children to find all the terminal nodes. - var node = workInProgress.child; + var child = bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); - while (node !== null) { - // eslint-disable-next-line no-labels - if (node.tag === HostComponent) { - var instance = node.stateNode; + if (child !== null) { + // The fallback children have pending work. Skip over the + // primary children and work on the fallback. + return child.sibling; + } else { + // Note: We can return `null` here because we already checked + // whether there were nested context consumers, via the call to + // `bailoutOnAlreadyFinishedWork` above. + return null; + } + } + } else { + pushSuspenseContext( + workInProgress, + setDefaultShallowSuspenseContext(suspenseStackCursor.current) + ); + } - if (needsVisibilityToggle && isHidden) { - // This child is inside a timed out tree. Hide it. - var props = node.memoizedProps; - var type = node.type; - instance = cloneHiddenInstance(instance); + break; } - appendChildToContainerChildSet(containerChildSet, instance); - } else if (node.tag === HostText) { - var _instance2 = node.stateNode; + case SuspenseListComponent: { + var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; - if (needsVisibilityToggle && isHidden) { - // This child is inside a timed out tree. Hide it. - var text = node.memoizedProps; - _instance2 = cloneHiddenTextInstance(); - } + var _hasChildWork = includesSomeLane( + renderLanes, + workInProgress.childLanes + ); - appendChildToContainerChildSet(containerChildSet, _instance2); - } else if (node.tag === HostPortal); - else if (node.tag === SuspenseComponent) { - if ((node.flags & Update) !== NoFlags) { - // Need to toggle the visibility of the primary children. - var newIsHidden = node.memoizedState !== null; + if (didSuspendBefore) { + if (_hasChildWork) { + // If something was in fallback state last time, and we have all the + // same children then we're still in progressive loading state. + // Something might get unblocked by state updates or retries in the + // tree which will affect the tail. So we need to use the normal + // path to compute the correct tail. + return updateSuspenseListComponent( + current, + workInProgress, + renderLanes + ); + } // If none of the children had any work, that means that none of + // them got retried so they'll still be blocked in the same way + // as before. We can fast bail out. - if (newIsHidden) { - var primaryChildParent = node.child; + workInProgress.flags |= DidCapture; + } // If nothing suspended before and we're rendering the same children, + // then the tail doesn't matter. Anything new that suspends will work + // in the "together" mode, so we can continue from the state we had. - if (primaryChildParent !== null) { - if (primaryChildParent.child !== null) { - primaryChildParent.child.return = primaryChildParent; - appendAllChildrenToContainer( - containerChildSet, - primaryChildParent, - true, - newIsHidden - ); - } + var renderState = workInProgress.memoizedState; - var fallbackChildParent = primaryChildParent.sibling; + if (renderState !== null) { + // Reset to the "together" mode in case we've started a different + // update in the past but didn't complete it. + renderState.rendering = null; + renderState.tail = null; + renderState.lastEffect = null; + } - if (fallbackChildParent !== null) { - fallbackChildParent.return = node; - node = fallbackChildParent; - continue; - } - } + pushSuspenseContext(workInProgress, suspenseStackCursor.current); + + if (_hasChildWork) { + break; + } else { + // If none of the children had any work, that means that none of + // them got retried so they'll still be blocked in the same way + // as before. We can fast bail out. + return null; } } - if (node.child !== null) { - // Continue traversing like normal - node.child.return = node; - node = node.child; - continue; + case OffscreenComponent: + case LegacyHiddenComponent: { + // Need to check if the tree still needs to be deferred. This is + // almost identical to the logic used in the normal update path, + // so we'll just enter that. The only difference is we'll bail out + // at the next level instead of this one, because the child props + // have not changed. Which is fine. + // TODO: Probably should refactor `beginWork` to split the bailout + // path from the normal path. I'm tempted to do a labeled break here + // but I won't :) + workInProgress.lanes = NoLanes; + return updateOffscreenComponent(current, workInProgress, renderLanes); } - } else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } // $FlowFixMe This is correct but Flow is confused by the labeled break. - - node = node; - - if (node === workInProgress) { - return; } - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; - } - - node = node.return; + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } else { + if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { + // This is a special case that only exists for legacy mode. + // See https://github.com/facebook/react/pull/19216. + didReceiveUpdate = true; + } else { + // An update was scheduled on this fiber, but there are no new props + // nor legacy context. Set this to false. If an update queue or context + // consumer produces a changed value, it will set this to true. Otherwise, + // the component will assume the children have not changed and bail out. + didReceiveUpdate = false; } - - node.sibling.return = node.return; - node = node.sibling; } - }; - - updateHostContainer = function(current, workInProgress) { - var portalOrRoot = workInProgress.stateNode; - var childrenUnchanged = hadNoMutationsEffects(current, workInProgress); - - if (childrenUnchanged); - else { - var container = portalOrRoot.containerInfo; - var newChildSet = createContainerChildSet(container); // If children might have changed, we have to add them all to the set. + } else { + didReceiveUpdate = false; + } // Before entering the begin phase, clear pending update priority. + // TODO: This assumes that we're about to evaluate the component and process + // the update queue. However, there's an exception: SimpleMemoComponent + // sometimes bails out later in the begin phase. This indicates that we should + // move this assignment out of the common path and into each branch. - appendAllChildrenToContainer(newChildSet, workInProgress, false, false); - portalOrRoot.pendingChildren = newChildSet; // Schedule an update on the container to swap out the container. + workInProgress.lanes = NoLanes; - markUpdate(workInProgress); - finalizeContainerChildren(container, newChildSet); + switch (workInProgress.tag) { + case IndeterminateComponent: { + return mountIndeterminateComponent( + current, + workInProgress, + workInProgress.type, + renderLanes + ); } - }; - updateHostComponent$1 = function( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ) { - var currentInstance = current.stateNode; - var oldProps = current.memoizedProps; // If there are no effects associated with this node, then none of our children had any updates. - // This guarantees that we can reuse all of them. - - var childrenUnchanged = hadNoMutationsEffects(current, workInProgress); + case LazyComponent: { + var elementType = workInProgress.elementType; + return mountLazyComponent( + current, + workInProgress, + elementType, + updateLanes, + renderLanes + ); + } - if (childrenUnchanged && oldProps === newProps) { - // No changes, just reuse the existing instance. - // Note that this might release a previous clone. - workInProgress.stateNode = currentInstance; - return; + case FunctionComponent: { + var _Component = workInProgress.type; + var unresolvedProps = workInProgress.pendingProps; + var resolvedProps = + workInProgress.elementType === _Component + ? unresolvedProps + : resolveDefaultProps(_Component, unresolvedProps); + return updateFunctionComponent( + current, + workInProgress, + _Component, + resolvedProps, + renderLanes + ); } - var recyclableInstance = workInProgress.stateNode; - var currentHostContext = getHostContext(); - var updatePayload = null; + case ClassComponent: { + var _Component2 = workInProgress.type; + var _unresolvedProps = workInProgress.pendingProps; - if (oldProps !== newProps) { - updatePayload = prepareUpdate( - recyclableInstance, - type, - oldProps, - newProps + var _resolvedProps = + workInProgress.elementType === _Component2 + ? _unresolvedProps + : resolveDefaultProps(_Component2, _unresolvedProps); + + return updateClassComponent( + current, + workInProgress, + _Component2, + _resolvedProps, + renderLanes ); } - if (childrenUnchanged && updatePayload === null) { - // No changes, just reuse the existing instance. - // Note that this might release a previous clone. - workInProgress.stateNode = currentInstance; - return; - } + case HostRoot: + return updateHostRoot(current, workInProgress, renderLanes); - var newInstance = cloneInstance( - currentInstance, - updatePayload, - type, - oldProps, - newProps, - workInProgress, - childrenUnchanged - ); + case HostComponent: + return updateHostComponent(current, workInProgress, renderLanes); - workInProgress.stateNode = newInstance; + case HostText: + return updateHostText(); - if (childrenUnchanged) { - // If there are no other effects in this tree, we need to flag this node as having one. - // Even though we're not going to use it for anything. - // Otherwise parents won't know that there are new children to propagate upwards. - markUpdate(workInProgress); - } else { - // If children might have changed, we have to add them all to the set. - appendAllChildren(newInstance, workInProgress, false, false); - } - }; + case SuspenseComponent: + return updateSuspenseComponent(current, workInProgress, renderLanes); - updateHostText$1 = function(current, workInProgress, oldText, newText) { - if (oldText !== newText) { - // If the text content differs, we'll create a new text instance for it. - var rootContainerInstance = getRootHostContainer(); - var currentHostContext = getHostContext(); - workInProgress.stateNode = createTextInstance( - newText, - rootContainerInstance, - currentHostContext, - workInProgress - ); // We'll have to mark it as having an effect, even though we won't use the effect for anything. - // This lets the parents know that at least one of their children has changed. + case HostPortal: + return updatePortalComponent(current, workInProgress, renderLanes); - markUpdate(workInProgress); - } else { - workInProgress.stateNode = current.stateNode; + case ForwardRef: { + var type = workInProgress.type; + var _unresolvedProps2 = workInProgress.pendingProps; + + var _resolvedProps2 = + workInProgress.elementType === type + ? _unresolvedProps2 + : resolveDefaultProps(type, _unresolvedProps2); + + return updateForwardRef( + current, + workInProgress, + type, + _resolvedProps2, + renderLanes + ); } - }; -} -function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { - switch (renderState.tailMode) { - case "hidden": { - // Any insertions at the end of the tail list after this point - // should be invisible. If there are already mounted boundaries - // anything before them are not considered for collapsing. - // Therefore we need to go through the whole tail to find if - // there are any. - var tailNode = renderState.tail; - var lastTailNode = null; + case Fragment: + return updateFragment(current, workInProgress, renderLanes); - while (tailNode !== null) { - if (tailNode.alternate !== null) { - lastTailNode = tailNode; - } + case Mode: + return updateMode(current, workInProgress, renderLanes); - tailNode = tailNode.sibling; - } // Next we're simply going to delete all insertions after the - // last rendered item. + case Profiler: + return updateProfiler(current, workInProgress, renderLanes); - if (lastTailNode === null) { - // All remaining items in the tail are insertions. - renderState.tail = null; - } else { - // Detach the insertion after the last node that was already - // inserted. - lastTailNode.sibling = null; - } + case ContextProvider: + return updateContextProvider(current, workInProgress, renderLanes); - break; - } + case ContextConsumer: + return updateContextConsumer(current, workInProgress, renderLanes); - case "collapsed": { - // Any insertions at the end of the tail list after this point - // should be invisible. If there are already mounted boundaries - // anything before them are not considered for collapsing. - // Therefore we need to go through the whole tail to find if - // there are any. - var _tailNode = renderState.tail; - var _lastTailNode = null; + case MemoComponent: { + var _type2 = workInProgress.type; + var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. - while (_tailNode !== null) { - if (_tailNode.alternate !== null) { - _lastTailNode = _tailNode; - } + var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); - _tailNode = _tailNode.sibling; - } // Next we're simply going to delete all insertions after the - // last rendered item. + { + if (workInProgress.type !== workInProgress.elementType) { + var outerPropTypes = _type2.propTypes; - if (_lastTailNode === null) { - // All remaining items in the tail are insertions. - if (!hasRenderedATailFallback && renderState.tail !== null) { - // We suspended during the head. We want to show at least one - // row at the tail. So we'll keep on and cut off the rest. - renderState.tail.sibling = null; - } else { - renderState.tail = null; + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + _resolvedProps3, // Resolved for outer only + "prop", + getComponentNameFromType(_type2) + ); + } } - } else { - // Detach the insertion after the last node that was already - // inserted. - _lastTailNode.sibling = null; } - break; + _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); + return updateMemoComponent( + current, + workInProgress, + _type2, + _resolvedProps3, + updateLanes, + renderLanes + ); } - } -} -function bubbleProperties(completedWork) { - var didBailout = - completedWork.alternate !== null && - completedWork.alternate.child === completedWork.child; - var newChildLanes = NoLanes; - var subtreeFlags = NoFlags; - - if (!didBailout) { - // Bubble up the earliest expiration time. - if ((completedWork.mode & ProfileMode) !== NoMode) { - // In profiling mode, resetChildExpirationTime is also used to reset - // profiler durations. - var actualDuration = completedWork.actualDuration; - var treeBaseDuration = completedWork.selfBaseDuration; - var child = completedWork.child; - - while (child !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(child.lanes, child.childLanes) - ); - subtreeFlags |= child.subtreeFlags; - subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will - // only be updated if work is done on the fiber (i.e. it doesn't bailout). - // When work is done, it should bubble to the parent's actualDuration. If - // the fiber has not been cloned though, (meaning no work was done), then - // this value will reflect the amount of time spent working on a previous - // render. In that case it should not bubble. We determine whether it was - // cloned by comparing the child pointer. + case SimpleMemoComponent: { + return updateSimpleMemoComponent( + current, + workInProgress, + workInProgress.type, + workInProgress.pendingProps, + updateLanes, + renderLanes + ); + } - actualDuration += child.actualDuration; - treeBaseDuration += child.treeBaseDuration; - child = child.sibling; - } + case IncompleteClassComponent: { + var _Component3 = workInProgress.type; + var _unresolvedProps4 = workInProgress.pendingProps; - completedWork.actualDuration = actualDuration; - completedWork.treeBaseDuration = treeBaseDuration; - } else { - var _child = completedWork.child; + var _resolvedProps4 = + workInProgress.elementType === _Component3 + ? _unresolvedProps4 + : resolveDefaultProps(_Component3, _unresolvedProps4); - while (_child !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child.lanes, _child.childLanes) - ); - subtreeFlags |= _child.subtreeFlags; - subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code - // smell because it assumes the commit phase is never concurrent with - // the render phase. Will address during refactor to alternate model. + return mountIncompleteClassComponent( + current, + workInProgress, + _Component3, + _resolvedProps4, + renderLanes + ); + } - _child.return = completedWork; - _child = _child.sibling; - } + case SuspenseListComponent: { + return updateSuspenseListComponent(current, workInProgress, renderLanes); } - completedWork.subtreeFlags |= subtreeFlags; - } else { - // Bubble up the earliest expiration time. - if ((completedWork.mode & ProfileMode) !== NoMode) { - // In profiling mode, resetChildExpirationTime is also used to reset - // profiler durations. - var _treeBaseDuration = completedWork.selfBaseDuration; - var _child2 = completedWork.child; + case ScopeComponent: { + break; + } - while (_child2 !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child2.lanes, _child2.childLanes) - ); // "Static" flags share the lifetime of the fiber/hook they belong to, - // so we should bubble those up even during a bailout. All the other - // flags have a lifetime only of a single render + commit, so we should - // ignore them. + case OffscreenComponent: { + return updateOffscreenComponent(current, workInProgress, renderLanes); + } - subtreeFlags |= _child2.subtreeFlags & StaticMask; - subtreeFlags |= _child2.flags & StaticMask; - _treeBaseDuration += _child2.treeBaseDuration; - _child2 = _child2.sibling; - } + case LegacyHiddenComponent: { + return updateLegacyHiddenComponent(current, workInProgress, renderLanes); + } + } - completedWork.treeBaseDuration = _treeBaseDuration; - } else { - var _child3 = completedWork.child; + { + throw Error( + "Unknown unit of work tag (" + + workInProgress.tag + + "). This error is likely caused by a bug in React. Please file an issue." + ); + } +} - while (_child3 !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child3.lanes, _child3.childLanes) - ); // "Static" flags share the lifetime of the fiber/hook they belong to, - // so we should bubble those up even during a bailout. All the other - // flags have a lifetime only of a single render + commit, so we should - // ignore them. +function markUpdate(workInProgress) { + // Tag the fiber with an update effect. This turns a Placement into + // a PlacementAndUpdate. + workInProgress.flags |= Update; +} - subtreeFlags |= _child3.subtreeFlags & StaticMask; - subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code - // smell because it assumes the commit phase is never concurrent with - // the render phase. Will address during refactor to alternate model. +function markRef$1(workInProgress) { + workInProgress.flags |= Ref; +} - _child3.return = completedWork; - _child3 = _child3.sibling; - } - } +function hadNoMutationsEffects(current, completedWork) { + var didBailout = current !== null && current.child === completedWork.child; - completedWork.subtreeFlags |= subtreeFlags; + if (didBailout) { + return true; } - completedWork.childLanes = newChildLanes; - return didBailout; -} + if ((completedWork.flags & ChildDeletion) !== NoFlags) { + return false; + } // TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties` + // then we only have to check the `completedWork.subtreeFlags`. -function completeWork(current, workInProgress, renderLanes) { - var newProps = workInProgress.pendingProps; + var child = completedWork.child; - switch (workInProgress.tag) { - case IndeterminateComponent: - case LazyComponent: - case SimpleMemoComponent: - case FunctionComponent: - case ForwardRef: - case Fragment: - case Mode: - case Profiler: - case ContextConsumer: - case MemoComponent: - bubbleProperties(workInProgress); - return null; + while (child !== null) { + if ( + (child.flags & MutationMask) !== NoFlags || + (child.subtreeFlags & MutationMask) !== NoFlags + ) { + return false; + } - case ClassComponent: { - var Component = workInProgress.type; + child = child.sibling; + } - if (isContextProvider(Component)) { - popContext(workInProgress); - } + return true; +} - bubbleProperties(workInProgress); - return null; - } +var appendAllChildren; +var updateHostContainer; +var updateHostComponent$1; +var updateHostText$1; - case HostRoot: { - var fiberRoot = workInProgress.stateNode; +{ + // Persistent host tree mode + appendAllChildren = function( + parent, + workInProgress, + needsVisibilityToggle, + isHidden + ) { + // We only have the top Fiber that was created but we need recurse down its + // children to find all the terminal nodes. + var node = workInProgress.child; - popHostContainer(workInProgress); - popTopLevelContextObject(workInProgress); - resetWorkInProgressVersions(); + while (node !== null) { + // eslint-disable-next-line no-labels + if (node.tag === HostComponent) { + var instance = node.stateNode; - if (fiberRoot.pendingContext) { - fiberRoot.context = fiberRoot.pendingContext; - fiberRoot.pendingContext = null; - } + if (needsVisibilityToggle && isHidden) { + // This child is inside a timed out tree. Hide it. + var props = node.memoizedProps; + var type = node.type; + instance = cloneHiddenInstance(instance); + } - if (current === null || current.child === null) { - // If we hydrated, pop so that we can delete any remaining children - // that weren't hydrated. - var wasHydrated = popHydrationState(); + appendInitialChild(parent, instance); + } else if (node.tag === HostText) { + var _instance = node.stateNode; - if (wasHydrated) { - // If we hydrated, then we'll need to schedule an update for - // the commit side-effects on the root. - markUpdate(workInProgress); - } else if (!fiberRoot.hydrate) { - // Schedule an effect to clear this container at the start of the next commit. - // This handles the case of React rendering into a container with previous children. - // It's also safe to do for updates too, because current.child would only be null - // if the previous render was null (so the the container would already be empty). - workInProgress.flags |= Snapshot; + if (needsVisibilityToggle && isHidden) { + // This child is inside a timed out tree. Hide it. + var text = node.memoizedProps; + _instance = cloneHiddenTextInstance(); } - } - updateHostContainer(current, workInProgress); - bubbleProperties(workInProgress); - return null; - } + appendInitialChild(parent, _instance); + } else if (node.tag === HostPortal); + else if (node.tag === SuspenseComponent) { + if ((node.flags & Update) !== NoFlags) { + // Need to toggle the visibility of the primary children. + var newIsHidden = node.memoizedState !== null; - case HostComponent: { - popHostContext(workInProgress); - var rootContainerInstance = getRootHostContainer(); - var type = workInProgress.type; + if (newIsHidden) { + var primaryChildParent = node.child; - if (current !== null && workInProgress.stateNode != null) { - updateHostComponent$1( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ); + if (primaryChildParent !== null) { + if (primaryChildParent.child !== null) { + primaryChildParent.child.return = primaryChildParent; + appendAllChildren( + parent, + primaryChildParent, + true, + newIsHidden + ); + } - if (current.ref !== workInProgress.ref) { - markRef$1(workInProgress); + var fallbackChildParent = primaryChildParent.sibling; + + if (fallbackChildParent !== null) { + fallbackChildParent.return = node; + node = fallbackChildParent; + continue; + } + } + } } - } else { - if (!newProps) { - if (!(workInProgress.stateNode !== null)) { - throw Error( - "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." - ); - } // This can happen when we abort work. - bubbleProperties(workInProgress); - return null; + if (node.child !== null) { + // Continue traversing like normal + node.child.return = node; + node = node.child; + continue; } + } else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; + } // $FlowFixMe This is correct but Flow is confused by the labeled break. - var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context - // "stack" as the parent. Then append children as we go in beginWork - // or completeWork depending on whether we want to add them top->down or - // bottom->up. Top->down is faster in IE11. + node = node; - var _wasHydrated = popHydrationState(); + if (node === workInProgress) { + return; + } - if (_wasHydrated) { - // TODO: Move this and createInstance step into the beginPhase - // to consolidate. - if (prepareToHydrateHostInstance()) { - // If changes to the hydrated node need to be applied at the - // commit-phase we mark this as such. - markUpdate(workInProgress); - } - } else { - var instance = createInstance( - type, - newProps, - rootContainerInstance, - currentHostContext, - workInProgress - ); - appendAllChildren(instance, workInProgress, false, false); - workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount. + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; } - if (workInProgress.ref !== null) { - // If there is a ref on a host node we need to schedule a callback - markRef$1(workInProgress); - } + node = node.return; } - bubbleProperties(workInProgress); - return null; + node.sibling.return = node.return; + node = node.sibling; } + }; // An unfortunate fork of appendAllChildren because we have two different parent types. - case HostText: { - var newText = newProps; + var appendAllChildrenToContainer = function( + containerChildSet, + workInProgress, + needsVisibilityToggle, + isHidden + ) { + // We only have the top Fiber that was created but we need recurse down its + // children to find all the terminal nodes. + var node = workInProgress.child; - if (current && workInProgress.stateNode != null) { - var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need - // to schedule a side-effect to do the updates. + while (node !== null) { + // eslint-disable-next-line no-labels + if (node.tag === HostComponent) { + var instance = node.stateNode; - updateHostText$1(current, workInProgress, oldText, newText); - } else { - if (typeof newText !== "string") { - if (!(workInProgress.stateNode !== null)) { - throw Error( - "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." - ); - } // This can happen when we abort work. + if (needsVisibilityToggle && isHidden) { + // This child is inside a timed out tree. Hide it. + var props = node.memoizedProps; + var type = node.type; + instance = cloneHiddenInstance(instance); + } + + appendChildToContainerChildSet(containerChildSet, instance); + } else if (node.tag === HostText) { + var _instance2 = node.stateNode; + + if (needsVisibilityToggle && isHidden) { + // This child is inside a timed out tree. Hide it. + var text = node.memoizedProps; + _instance2 = cloneHiddenTextInstance(); } - var _rootContainerInstance = getRootHostContainer(); + appendChildToContainerChildSet(containerChildSet, _instance2); + } else if (node.tag === HostPortal); + else if (node.tag === SuspenseComponent) { + if ((node.flags & Update) !== NoFlags) { + // Need to toggle the visibility of the primary children. + var newIsHidden = node.memoizedState !== null; - var _currentHostContext = getHostContext(); + if (newIsHidden) { + var primaryChildParent = node.child; - var _wasHydrated2 = popHydrationState(); + if (primaryChildParent !== null) { + if (primaryChildParent.child !== null) { + primaryChildParent.child.return = primaryChildParent; + appendAllChildrenToContainer( + containerChildSet, + primaryChildParent, + true, + newIsHidden + ); + } - if (_wasHydrated2) { - if (prepareToHydrateHostTextInstance()) { - markUpdate(workInProgress); + var fallbackChildParent = primaryChildParent.sibling; + + if (fallbackChildParent !== null) { + fallbackChildParent.return = node; + node = fallbackChildParent; + continue; + } + } } - } else { - workInProgress.stateNode = createTextInstance( - newText, - _rootContainerInstance, - _currentHostContext, - workInProgress - ); } - } - - bubbleProperties(workInProgress); - return null; - } - - case SuspenseComponent: { - popSuspenseContext(workInProgress); - var nextState = workInProgress.memoizedState; - if ((workInProgress.flags & DidCapture) !== NoFlags) { - // Something suspended. Re-render with the fallback children. - workInProgress.lanes = renderLanes; // Do not reset the effect list. + if (node.child !== null) { + // Continue traversing like normal + node.child.return = node; + node = node.child; + continue; + } + } else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; + } // $FlowFixMe This is correct but Flow is confused by the labeled break. - if ((workInProgress.mode & ProfileMode) !== NoMode) { - transferActualDuration(workInProgress); - } // Don't bubble properties in this case. + node = node; - return workInProgress; + if (node === workInProgress) { + return; } - var nextDidTimeout = nextState !== null; - var prevDidTimeout = false; + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; + } - if (current === null) { - if (workInProgress.memoizedProps.fallback !== undefined); - } else { - var prevState = current.memoizedState; - prevDidTimeout = prevState !== null; + node = node.return; } - if (nextDidTimeout && !prevDidTimeout) { - // TODO: This will still suspend a synchronous tree if anything - // in the concurrent tree already suspended during this render. - // This is a known bug. - if ((workInProgress.mode & ConcurrentMode) !== NoMode) { - // TODO: Move this back to throwException because this is too late - // if this is a large tree which is common for initial loads. We - // don't know if we should restart a render or not until we get - // this marker, and this is too late. - // If this render already had a ping or lower pri updates, - // and this is the first time we know we're going to suspend we - // should be able to immediately restart from within throwException. - var hasInvisibleChildContext = - current === null && - workInProgress.memoizedProps.unstable_avoidThisFallback !== true; + node.sibling.return = node.return; + node = node.sibling; + } + }; - if ( - hasInvisibleChildContext || - hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext - ) - ) { - // If this was in an invisible tree or a new render, then showing - // this boundary is ok. - renderDidSuspend(); - } else { - // Otherwise, we're going to have to hide content so we should - // suspend for longer if possible. - renderDidSuspendDelayIfPossible(); - } - } - } + updateHostContainer = function(current, workInProgress) { + var portalOrRoot = workInProgress.stateNode; + var childrenUnchanged = hadNoMutationsEffects(current, workInProgress); - { - // TODO: Only schedule updates if not prevDidTimeout. - if (nextDidTimeout) { - // If this boundary just timed out, schedule an effect to attach a - // retry listener to the promise. This flag is also used to hide the - // primary children. - workInProgress.flags |= Update; - } - } + if (childrenUnchanged); + else { + var container = portalOrRoot.containerInfo; + var newChildSet = createContainerChildSet(container); // If children might have changed, we have to add them all to the set. - bubbleProperties(workInProgress); + appendAllChildrenToContainer(newChildSet, workInProgress, false, false); + portalOrRoot.pendingChildren = newChildSet; // Schedule an update on the container to swap out the container. - { - if ((workInProgress.mode & ProfileMode) !== NoMode) { - if (nextDidTimeout) { - // Don't count time spent in a timed out Suspense subtree as part of the base duration. - var _primaryChildFragment2 = workInProgress.child; + markUpdate(workInProgress); + finalizeContainerChildren(container, newChildSet); + } + }; - if (_primaryChildFragment2 !== null) { - // $FlowFixMe Flow doesn't support type casting in combination with the -= operator - workInProgress.treeBaseDuration -= - _primaryChildFragment2.treeBaseDuration; - } - } - } - } + updateHostComponent$1 = function( + current, + workInProgress, + type, + newProps, + rootContainerInstance + ) { + var currentInstance = current.stateNode; + var oldProps = current.memoizedProps; // If there are no effects associated with this node, then none of our children had any updates. + // This guarantees that we can reuse all of them. - return null; - } + var childrenUnchanged = hadNoMutationsEffects(current, workInProgress); - case HostPortal: - popHostContainer(workInProgress); - updateHostContainer(current, workInProgress); + if (childrenUnchanged && oldProps === newProps) { + // No changes, just reuse the existing instance. + // Note that this might release a previous clone. + workInProgress.stateNode = currentInstance; + return; + } - if (current === null) { - preparePortalMount(workInProgress.stateNode.containerInfo); - } + var recyclableInstance = workInProgress.stateNode; + var currentHostContext = getHostContext(); + var updatePayload = null; - bubbleProperties(workInProgress); - return null; + if (oldProps !== newProps) { + updatePayload = prepareUpdate( + recyclableInstance, + type, + oldProps, + newProps + ); + } - case ContextProvider: - // Pop provider fiber - var context = workInProgress.type._context; - popProvider(context, workInProgress); - bubbleProperties(workInProgress); - return null; + if (childrenUnchanged && updatePayload === null) { + // No changes, just reuse the existing instance. + // Note that this might release a previous clone. + workInProgress.stateNode = currentInstance; + return; + } - case IncompleteClassComponent: { - // Same as class component case. I put it down here so that the tags are - // sequential to ensure this switch is compiled to a jump table. - var _Component = workInProgress.type; + var newInstance = cloneInstance( + currentInstance, + updatePayload, + type, + oldProps, + newProps, + workInProgress, + childrenUnchanged + ); - if (isContextProvider(_Component)) { - popContext(workInProgress); - } + workInProgress.stateNode = newInstance; - bubbleProperties(workInProgress); - return null; + if (childrenUnchanged) { + // If there are no other effects in this tree, we need to flag this node as having one. + // Even though we're not going to use it for anything. + // Otherwise parents won't know that there are new children to propagate upwards. + markUpdate(workInProgress); + } else { + // If children might have changed, we have to add them all to the set. + appendAllChildren(newInstance, workInProgress, false, false); } + }; - case SuspenseListComponent: { - popSuspenseContext(workInProgress); - var renderState = workInProgress.memoizedState; + updateHostText$1 = function(current, workInProgress, oldText, newText) { + if (oldText !== newText) { + // If the text content differs, we'll create a new text instance for it. + var rootContainerInstance = getRootHostContainer(); + var currentHostContext = getHostContext(); + workInProgress.stateNode = createTextInstance( + newText, + rootContainerInstance, + currentHostContext, + workInProgress + ); // We'll have to mark it as having an effect, even though we won't use the effect for anything. + // This lets the parents know that at least one of their children has changed. + + markUpdate(workInProgress); + } else { + workInProgress.stateNode = current.stateNode; + } + }; +} - if (renderState === null) { - // We're running in the default, "independent" mode. - // We don't do anything in this mode. - bubbleProperties(workInProgress); - return null; - } +function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { + switch (renderState.tailMode) { + case "hidden": { + // Any insertions at the end of the tail list after this point + // should be invisible. If there are already mounted boundaries + // anything before them are not considered for collapsing. + // Therefore we need to go through the whole tail to find if + // there are any. + var tailNode = renderState.tail; + var lastTailNode = null; - var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; - var renderedTail = renderState.rendering; + while (tailNode !== null) { + if (tailNode.alternate !== null) { + lastTailNode = tailNode; + } - if (renderedTail === null) { - // We just rendered the head. - if (!didSuspendAlready) { - // This is the first pass. We need to figure out if anything is still - // suspended in the rendered set. - // If new content unsuspended, but there's still some content that - // didn't. Then we need to do a second pass that forces everything - // to keep showing their fallbacks. - // We might be suspended if something in this render pass suspended, or - // something in the previous committed pass suspended. Otherwise, - // there's no chance so we can skip the expensive call to - // findFirstSuspended. - var cannotBeSuspended = - renderHasNotSuspendedYet() && - (current === null || (current.flags & DidCapture) === NoFlags); + tailNode = tailNode.sibling; + } // Next we're simply going to delete all insertions after the + // last rendered item. - if (!cannotBeSuspended) { - var row = workInProgress.child; + if (lastTailNode === null) { + // All remaining items in the tail are insertions. + renderState.tail = null; + } else { + // Detach the insertion after the last node that was already + // inserted. + lastTailNode.sibling = null; + } - while (row !== null) { - var suspended = findFirstSuspended(row); + break; + } - if (suspended !== null) { - didSuspendAlready = true; - workInProgress.flags |= DidCapture; - cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as - // part of the second pass. In that case nothing will subscribe to - // its thennables. Instead, we'll transfer its thennables to the - // SuspenseList so that it can retry if they resolve. - // There might be multiple of these in the list but since we're - // going to wait for all of them anyway, it doesn't really matter - // which ones gets to ping. In theory we could get clever and keep - // track of how many dependencies remain but it gets tricky because - // in the meantime, we can add/remove/change items and dependencies. - // We might bail out of the loop before finding any but that - // doesn't matter since that means that the other boundaries that - // we did find already has their listeners attached. + case "collapsed": { + // Any insertions at the end of the tail list after this point + // should be invisible. If there are already mounted boundaries + // anything before them are not considered for collapsing. + // Therefore we need to go through the whole tail to find if + // there are any. + var _tailNode = renderState.tail; + var _lastTailNode = null; - var newThennables = suspended.updateQueue; + while (_tailNode !== null) { + if (_tailNode.alternate !== null) { + _lastTailNode = _tailNode; + } - if (newThennables !== null) { - workInProgress.updateQueue = newThennables; - workInProgress.flags |= Update; - } // Rerender the whole list, but this time, we'll force fallbacks - // to stay in place. - // Reset the effect flags before doing the second pass since that's now invalid. - // Reset the child fibers to their original state. + _tailNode = _tailNode.sibling; + } // Next we're simply going to delete all insertions after the + // last rendered item. - workInProgress.subtreeFlags = NoFlags; - resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately - // rerender the children. + if (_lastTailNode === null) { + // All remaining items in the tail are insertions. + if (!hasRenderedATailFallback && renderState.tail !== null) { + // We suspended during the head. We want to show at least one + // row at the tail. So we'll keep on and cut off the rest. + renderState.tail.sibling = null; + } else { + renderState.tail = null; + } + } else { + // Detach the insertion after the last node that was already + // inserted. + _lastTailNode.sibling = null; + } - pushSuspenseContext( - workInProgress, - setShallowSuspenseContext( - suspenseStackCursor.current, - ForceSuspenseFallback - ) - ); // Don't bubble properties in this case. + break; + } + } +} - return workInProgress.child; - } +function bubbleProperties(completedWork) { + var didBailout = + completedWork.alternate !== null && + completedWork.alternate.child === completedWork.child; + var newChildLanes = NoLanes; + var subtreeFlags = NoFlags; - row = row.sibling; - } - } + if (!didBailout) { + // Bubble up the earliest expiration time. + if ((completedWork.mode & ProfileMode) !== NoMode) { + // In profiling mode, resetChildExpirationTime is also used to reset + // profiler durations. + var actualDuration = completedWork.actualDuration; + var treeBaseDuration = completedWork.selfBaseDuration; + var child = completedWork.child; - if (renderState.tail !== null && now() > getRenderTargetTime()) { - // We have already passed our CPU deadline but we still have rows - // left in the tail. We'll just give up further attempts to render - // the main content and only render fallbacks. - workInProgress.flags |= DidCapture; - didSuspendAlready = true; - cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this - // to get it started back up to attempt the next item. While in terms - // of priority this work has the same priority as this current render, - // it's not part of the same transition once the transition has - // committed. If it's sync, we still want to yield so that it can be - // painted. Conceptually, this is really the same as pinging. - // We can use any RetryLane even if it's the one currently rendering - // since we're leaving it behind on this node. + while (child !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(child.lanes, child.childLanes) + ); + subtreeFlags |= child.subtreeFlags; + subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will + // only be updated if work is done on the fiber (i.e. it doesn't bailout). + // When work is done, it should bubble to the parent's actualDuration. If + // the fiber has not been cloned though, (meaning no work was done), then + // this value will reflect the amount of time spent working on a previous + // render. In that case it should not bubble. We determine whether it was + // cloned by comparing the child pointer. - workInProgress.lanes = SomeRetryLane; - } - } else { - cutOffTailIfNeeded(renderState, false); - } // Next we're going to render the tail. - } else { - // Append the rendered row to the child list. - if (!didSuspendAlready) { - var _suspended = findFirstSuspended(renderedTail); + actualDuration += child.actualDuration; + treeBaseDuration += child.treeBaseDuration; + child = child.sibling; + } - if (_suspended !== null) { - workInProgress.flags |= DidCapture; - didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't - // get lost if this row ends up dropped during a second pass. + completedWork.actualDuration = actualDuration; + completedWork.treeBaseDuration = treeBaseDuration; + } else { + var _child = completedWork.child; - var _newThennables = _suspended.updateQueue; + while (_child !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child.lanes, _child.childLanes) + ); + subtreeFlags |= _child.subtreeFlags; + subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code + // smell because it assumes the commit phase is never concurrent with + // the render phase. Will address during refactor to alternate model. - if (_newThennables !== null) { - workInProgress.updateQueue = _newThennables; - workInProgress.flags |= Update; - } + _child.return = completedWork; + _child = _child.sibling; + } + } - cutOffTailIfNeeded(renderState, true); // This might have been modified. + completedWork.subtreeFlags |= subtreeFlags; + } else { + // Bubble up the earliest expiration time. + if ((completedWork.mode & ProfileMode) !== NoMode) { + // In profiling mode, resetChildExpirationTime is also used to reset + // profiler durations. + var _treeBaseDuration = completedWork.selfBaseDuration; + var _child2 = completedWork.child; - if ( - renderState.tail === null && - renderState.tailMode === "hidden" && - !renderedTail.alternate && - !getIsHydrating() // We don't cut it if we're hydrating. - ) { - // We're done. - bubbleProperties(workInProgress); - return null; - } - } else if ( - // The time it took to render last row is greater than the remaining - // time we have to render. So rendering one more row would likely - // exceed it. - now() * 2 - renderState.renderingStartTime > - getRenderTargetTime() && - renderLanes !== OffscreenLane - ) { - // We have now passed our CPU deadline and we'll just give up further - // attempts to render the main content and only render fallbacks. - // The assumption is that this is usually faster. - workInProgress.flags |= DidCapture; - didSuspendAlready = true; - cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this - // to get it started back up to attempt the next item. While in terms - // of priority this work has the same priority as this current render, - // it's not part of the same transition once the transition has - // committed. If it's sync, we still want to yield so that it can be - // painted. Conceptually, this is really the same as pinging. - // We can use any RetryLane even if it's the one currently rendering - // since we're leaving it behind on this node. + while (_child2 !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child2.lanes, _child2.childLanes) + ); // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. - workInProgress.lanes = SomeRetryLane; - } - } + subtreeFlags |= _child2.subtreeFlags & StaticMask; + subtreeFlags |= _child2.flags & StaticMask; + _treeBaseDuration += _child2.treeBaseDuration; + _child2 = _child2.sibling; + } - if (renderState.isBackwards) { - // The effect list of the backwards tail will have been added - // to the end. This breaks the guarantee that life-cycles fire in - // sibling order but that isn't a strong guarantee promised by React. - // Especially since these might also just pop in during future commits. - // Append to the beginning of the list. - renderedTail.sibling = workInProgress.child; - workInProgress.child = renderedTail; - } else { - var previousSibling = renderState.last; + completedWork.treeBaseDuration = _treeBaseDuration; + } else { + var _child3 = completedWork.child; - if (previousSibling !== null) { - previousSibling.sibling = renderedTail; - } else { - workInProgress.child = renderedTail; - } + while (_child3 !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child3.lanes, _child3.childLanes) + ); // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. - renderState.last = renderedTail; - } + subtreeFlags |= _child3.subtreeFlags & StaticMask; + subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code + // smell because it assumes the commit phase is never concurrent with + // the render phase. Will address during refactor to alternate model. + + _child3.return = completedWork; + _child3 = _child3.sibling; } + } - if (renderState.tail !== null) { - // We still have tail rows to render. - // Pop a row. - var next = renderState.tail; - renderState.rendering = next; - renderState.tail = next.sibling; - renderState.renderingStartTime = now(); - next.sibling = null; // Restore the context. - // TODO: We can probably just avoid popping it instead and only - // setting it the first time we go from not suspended to suspended. + completedWork.subtreeFlags |= subtreeFlags; + } - var suspenseContext = suspenseStackCursor.current; + completedWork.childLanes = newChildLanes; + return didBailout; +} - if (didSuspendAlready) { - suspenseContext = setShallowSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); - } else { - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - } +function completeWork(current, workInProgress, renderLanes) { + var newProps = workInProgress.pendingProps; - pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. - // Don't bubble properties in this case. + switch (workInProgress.tag) { + case IndeterminateComponent: + case LazyComponent: + case SimpleMemoComponent: + case FunctionComponent: + case ForwardRef: + case Fragment: + case Mode: + case Profiler: + case ContextConsumer: + case MemoComponent: + bubbleProperties(workInProgress); + return null; - return next; + case ClassComponent: { + var Component = workInProgress.type; + + if (isContextProvider(Component)) { + popContext(workInProgress); } bubbleProperties(workInProgress); return null; } - case ScopeComponent: { - break; - } + case HostRoot: { + var fiberRoot = workInProgress.stateNode; - case OffscreenComponent: - case LegacyHiddenComponent: { - popRenderLanes(workInProgress); - var _nextState = workInProgress.memoizedState; - var nextIsHidden = _nextState !== null; + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + resetWorkInProgressVersions(); - if (current !== null) { - var _prevState = current.memoizedState; - var prevIsHidden = _prevState !== null; + if (fiberRoot.pendingContext) { + fiberRoot.context = fiberRoot.pendingContext; + fiberRoot.pendingContext = null; + } - if ( - prevIsHidden !== nextIsHidden && - newProps.mode !== "unstable-defer-without-hiding" - ) { - workInProgress.flags |= Update; - } - } // Don't bubble properties for hidden children. + if (current === null || current.child === null) { + // If we hydrated, pop so that we can delete any remaining children + // that weren't hydrated. + var wasHydrated = popHydrationState(); - if ( - !nextIsHidden || - includesSomeLane(subtreeRenderLanes, OffscreenLane) || - (workInProgress.mode & ConcurrentMode) === NoMode - ) { - bubbleProperties(workInProgress); + if (wasHydrated) { + // If we hydrated, then we'll need to schedule an update for + // the commit side-effects on the root. + markUpdate(workInProgress); + } else if (!fiberRoot.hydrate) { + // Schedule an effect to clear this container at the start of the next commit. + // This handles the case of React rendering into a container with previous children. + // It's also safe to do for updates too, because current.child would only be null + // if the previous render was null (so the the container would already be empty). + workInProgress.flags |= Snapshot; + } } + updateHostContainer(current, workInProgress); + bubbleProperties(workInProgress); return null; } - } - { - throw Error( - "Unknown unit of work tag (" + - workInProgress.tag + - "). This error is likely caused by a bug in React. Please file an issue." - ); - } -} + case HostComponent: { + popHostContext(workInProgress); + var rootContainerInstance = getRootHostContainer(); + var type = workInProgress.type; -function unwindWork(workInProgress, renderLanes) { - switch (workInProgress.tag) { - case ClassComponent: { - var Component = workInProgress.type; + if (current !== null && workInProgress.stateNode != null) { + updateHostComponent$1( + current, + workInProgress, + type, + newProps, + rootContainerInstance + ); - if (isContextProvider(Component)) { - popContext(workInProgress); - } + if (current.ref !== workInProgress.ref) { + markRef$1(workInProgress); + } + } else { + if (!newProps) { + if (!(workInProgress.stateNode !== null)) { + throw Error( + "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." + ); + } // This can happen when we abort work. - var flags = workInProgress.flags; + bubbleProperties(workInProgress); + return null; + } - if (flags & ShouldCapture) { - workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; + var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context + // "stack" as the parent. Then append children as we go in beginWork + // or completeWork depending on whether we want to add them top->down or + // bottom->up. Top->down is faster in IE11. - if ((workInProgress.mode & ProfileMode) !== NoMode) { - transferActualDuration(workInProgress); + var _wasHydrated = popHydrationState(); + + if (_wasHydrated) { + // TODO: Move this and createInstance step into the beginPhase + // to consolidate. + if (prepareToHydrateHostInstance()) { + // If changes to the hydrated node need to be applied at the + // commit-phase we mark this as such. + markUpdate(workInProgress); + } + } else { + var instance = createInstance( + type, + newProps, + rootContainerInstance, + currentHostContext, + workInProgress + ); + appendAllChildren(instance, workInProgress, false, false); + workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount. } - return workInProgress; + if (workInProgress.ref !== null) { + // If there is a ref on a host node we need to schedule a callback + markRef$1(workInProgress); + } } + bubbleProperties(workInProgress); return null; } - case HostRoot: { - popHostContainer(workInProgress); - popTopLevelContextObject(workInProgress); - resetWorkInProgressVersions(); - var _flags = workInProgress.flags; + case HostText: { + var newText = newProps; - if (!((_flags & DidCapture) === NoFlags)) { - throw Error( - "The root failed to unmount after an error. This is likely a bug in React. Please file an issue." - ); + if (current && workInProgress.stateNode != null) { + var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need + // to schedule a side-effect to do the updates. + + updateHostText$1(current, workInProgress, oldText, newText); + } else { + if (typeof newText !== "string") { + if (!(workInProgress.stateNode !== null)) { + throw Error( + "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." + ); + } // This can happen when we abort work. + } + + var _rootContainerInstance = getRootHostContainer(); + + var _currentHostContext = getHostContext(); + + var _wasHydrated2 = popHydrationState(); + + if (_wasHydrated2) { + if (prepareToHydrateHostTextInstance()) { + markUpdate(workInProgress); + } + } else { + workInProgress.stateNode = createTextInstance( + newText, + _rootContainerInstance, + _currentHostContext, + workInProgress + ); + } } - workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture; - return workInProgress; - } - - case HostComponent: { - // TODO: popHydrationState - popHostContext(workInProgress); + bubbleProperties(workInProgress); return null; } case SuspenseComponent: { popSuspenseContext(workInProgress); + var nextState = workInProgress.memoizedState; - var _flags2 = workInProgress.flags; - - if (_flags2 & ShouldCapture) { - workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary. + if ((workInProgress.flags & DidCapture) !== NoFlags) { + // Something suspended. Re-render with the fallback children. + workInProgress.lanes = renderLanes; // Do not reset the effect list. if ((workInProgress.mode & ProfileMode) !== NoMode) { transferActualDuration(workInProgress); - } + } // Don't bubble properties in this case. return workInProgress; } - return null; - } + var nextDidTimeout = nextState !== null; + var prevDidTimeout = false; - case SuspenseListComponent: { - popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been - // caught by a nested boundary. If not, it should bubble through. + if (current === null) { + if (workInProgress.memoizedProps.fallback !== undefined); + } else { + var prevState = current.memoizedState; + prevDidTimeout = prevState !== null; + } + + if (nextDidTimeout && !prevDidTimeout) { + // TODO: This will still suspend a synchronous tree if anything + // in the concurrent tree already suspended during this render. + // This is a known bug. + if ((workInProgress.mode & ConcurrentMode) !== NoMode) { + // TODO: Move this back to throwException because this is too late + // if this is a large tree which is common for initial loads. We + // don't know if we should restart a render or not until we get + // this marker, and this is too late. + // If this render already had a ping or lower pri updates, + // and this is the first time we know we're going to suspend we + // should be able to immediately restart from within throwException. + var hasInvisibleChildContext = + current === null && + workInProgress.memoizedProps.unstable_avoidThisFallback !== true; + + if ( + hasInvisibleChildContext || + hasSuspenseContext( + suspenseStackCursor.current, + InvisibleParentSuspenseContext + ) + ) { + // If this was in an invisible tree or a new render, then showing + // this boundary is ok. + renderDidSuspend(); + } else { + // Otherwise, we're going to have to hide content so we should + // suspend for longer if possible. + renderDidSuspendDelayIfPossible(); + } + } + } + + { + // TODO: Only schedule updates if not prevDidTimeout. + if (nextDidTimeout) { + // If this boundary just timed out, schedule an effect to attach a + // retry listener to the promise. This flag is also used to hide the + // primary children. + workInProgress.flags |= Update; + } + } + + bubbleProperties(workInProgress); + + { + if ((workInProgress.mode & ProfileMode) !== NoMode) { + if (nextDidTimeout) { + // Don't count time spent in a timed out Suspense subtree as part of the base duration. + var _primaryChildFragment2 = workInProgress.child; + + if (_primaryChildFragment2 !== null) { + // $FlowFixMe Flow doesn't support type casting in combination with the -= operator + workInProgress.treeBaseDuration -= + _primaryChildFragment2.treeBaseDuration; + } + } + } + } return null; } case HostPortal: popHostContainer(workInProgress); + updateHostContainer(current, workInProgress); + + if (current === null) { + preparePortalMount(workInProgress.stateNode.containerInfo); + } + + bubbleProperties(workInProgress); return null; case ContextProvider: + // Pop provider fiber var context = workInProgress.type._context; popProvider(context, workInProgress); + bubbleProperties(workInProgress); return null; - case OffscreenComponent: - case LegacyHiddenComponent: - popRenderLanes(workInProgress); - - return null; - - case CacheComponent: - return null; - - default: - return null; - } -} - -function unwindInterruptedWork(interruptedWork, renderLanes) { - switch (interruptedWork.tag) { - case ClassComponent: { - var childContextTypes = interruptedWork.type.childContextTypes; + case IncompleteClassComponent: { + // Same as class component case. I put it down here so that the tags are + // sequential to ensure this switch is compiled to a jump table. + var _Component = workInProgress.type; - if (childContextTypes !== null && childContextTypes !== undefined) { - popContext(interruptedWork); + if (isContextProvider(_Component)) { + popContext(workInProgress); } - break; - } - - case HostRoot: { - popHostContainer(interruptedWork); - popTopLevelContextObject(interruptedWork); - resetWorkInProgressVersions(); - break; - } - - case HostComponent: { - popHostContext(interruptedWork); - break; + bubbleProperties(workInProgress); + return null; } - case HostPortal: - popHostContainer(interruptedWork); - break; - - case SuspenseComponent: - popSuspenseContext(interruptedWork); - break; - - case SuspenseListComponent: - popSuspenseContext(interruptedWork); - break; + case SuspenseListComponent: { + popSuspenseContext(workInProgress); + var renderState = workInProgress.memoizedState; - case ContextProvider: - var context = interruptedWork.type._context; - popProvider(context, interruptedWork); - break; + if (renderState === null) { + // We're running in the default, "independent" mode. + // We don't do anything in this mode. + bubbleProperties(workInProgress); + return null; + } - case OffscreenComponent: - case LegacyHiddenComponent: - popRenderLanes(interruptedWork); + var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; + var renderedTail = renderState.rendering; - break; - } -} + if (renderedTail === null) { + // We just rendered the head. + if (!didSuspendAlready) { + // This is the first pass. We need to figure out if anything is still + // suspended in the rendered set. + // If new content unsuspended, but there's still some content that + // didn't. Then we need to do a second pass that forces everything + // to keep showing their fallbacks. + // We might be suspended if something in this render pass suspended, or + // something in the previous committed pass suspended. Otherwise, + // there's no chance so we can skip the expensive call to + // findFirstSuspended. + var cannotBeSuspended = + renderHasNotSuspendedYet() && + (current === null || (current.flags & DidCapture) === NoFlags); -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: getStackByFiberInDevAndProd(source) - }; -} + if (!cannotBeSuspended) { + var row = workInProgress.child; -if ( - !( - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog === - "function" - ) -) { - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -} + while (row !== null) { + var suspended = findFirstSuspended(row); -function showErrorDialog(boundary, errorInfo) { - var capturedError = { - componentStack: errorInfo.stack !== null ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - boundary !== null && boundary.tag === ClassComponent - ? boundary.stateNode - : null - }; - return ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog( - capturedError - ); -} + if (suspended !== null) { + didSuspendAlready = true; + workInProgress.flags |= DidCapture; + cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as + // part of the second pass. In that case nothing will subscribe to + // its thennables. Instead, we'll transfer its thennables to the + // SuspenseList so that it can retry if they resolve. + // There might be multiple of these in the list but since we're + // going to wait for all of them anyway, it doesn't really matter + // which ones gets to ping. In theory we could get clever and keep + // track of how many dependencies remain but it gets tricky because + // in the meantime, we can add/remove/change items and dependencies. + // We might bail out of the loop before finding any but that + // doesn't matter since that means that the other boundaries that + // we did find already has their listeners attached. -function logCapturedError(boundary, errorInfo) { - try { - var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. - // This enables renderers like ReactNative to better manage redbox behavior. + var newThennables = suspended.updateQueue; - if (logError === false) { - return; - } + if (newThennables !== null) { + workInProgress.updateQueue = newThennables; + workInProgress.flags |= Update; + } // Rerender the whole list, but this time, we'll force fallbacks + // to stay in place. + // Reset the effect flags before doing the second pass since that's now invalid. + // Reset the child fibers to their original state. - var error = errorInfo.value; + workInProgress.subtreeFlags = NoFlags; + resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately + // rerender the children. - if (true) { - var source = errorInfo.source; - var stack = errorInfo.stack; - var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling - // `preventDefault()` in window `error` handler. - // We record this information as an expando on the error. + pushSuspenseContext( + workInProgress, + setShallowSuspenseContext( + suspenseStackCursor.current, + ForceSuspenseFallback + ) + ); // Don't bubble properties in this case. - if (error != null && error._suppressLogging) { - if (boundary.tag === ClassComponent) { - // The error is recoverable and was silenced. - // Ignore it and don't print the stack addendum. - // This is handy for testing error boundaries without noise. - return; - } // The error is fatal. Since the silencing might have - // been accidental, we'll surface it anyway. - // However, the browser would have silenced the original error - // so we'll print it first, and then print the stack addendum. + return workInProgress.child; + } - console["error"](error); // Don't transform to our wrapper - // For a more detailed description of this block, see: - // https://github.com/facebook/react/pull/13384 - } + row = row.sibling; + } + } - var componentName = source ? getComponentNameFromFiber(source) : null; - var componentNameMessage = componentName - ? "The above error occurred in the <" + componentName + "> component:" - : "The above error occurred in one of your React components:"; - var errorBoundaryMessage; + if (renderState.tail !== null && now() > getRenderTargetTime()) { + // We have already passed our CPU deadline but we still have rows + // left in the tail. We'll just give up further attempts to render + // the main content and only render fallbacks. + workInProgress.flags |= DidCapture; + didSuspendAlready = true; + cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this + // to get it started back up to attempt the next item. While in terms + // of priority this work has the same priority as this current render, + // it's not part of the same transition once the transition has + // committed. If it's sync, we still want to yield so that it can be + // painted. Conceptually, this is really the same as pinging. + // We can use any RetryLane even if it's the one currently rendering + // since we're leaving it behind on this node. - if (boundary.tag === HostRoot) { - errorBoundaryMessage = - "Consider adding an error boundary to your tree to customize error handling behavior.\n" + - "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries."; + workInProgress.lanes = SomeRetryLane; + } + } else { + cutOffTailIfNeeded(renderState, false); + } // Next we're going to render the tail. } else { - var errorBoundaryName = - getComponentNameFromFiber(boundary) || "Anonymous"; - errorBoundaryMessage = - "React will try to recreate this component tree from scratch " + - ("using the error boundary you provided, " + errorBoundaryName + "."); - } - - var combinedMessage = - componentNameMessage + - "\n" + - 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. + // Append the rendered row to the child list. + if (!didSuspendAlready) { + var _suspended = findFirstSuspended(renderedTail); - console["error"](combinedMessage); // Don't transform to our wrapper - } else { - // In production, we print the error directly. - // This will include the message, the JS stack, and anything the browser wants to show. - // We pass the error object instead of custom message so that the browser displays the error natively. - console["error"](error); // Don't transform to our wrapper - } - } catch (e) { - // This method must not throw, or React internal state will get messed up. - // If console.error is overridden, or logCapturedError() shows a dialog that throws, - // we want to report this error outside of the normal stack as a last resort. - // https://github.com/facebook/react/issues/13188 - setTimeout(function() { - throw e; - }); - } -} + if (_suspended !== null) { + workInProgress.flags |= DidCapture; + didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't + // get lost if this row ends up dropped during a second pass. -var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; + var _newThennables = _suspended.updateQueue; -function createRootErrorUpdate(fiber, errorInfo, lane) { - var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. + if (_newThennables !== null) { + workInProgress.updateQueue = _newThennables; + workInProgress.flags |= Update; + } - update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property - // being called "element". + cutOffTailIfNeeded(renderState, true); // This might have been modified. - update.payload = { - element: null - }; - var error = errorInfo.value; + if ( + renderState.tail === null && + renderState.tailMode === "hidden" && + !renderedTail.alternate && + !getIsHydrating() // We don't cut it if we're hydrating. + ) { + // We're done. + bubbleProperties(workInProgress); + return null; + } + } else if ( + // The time it took to render last row is greater than the remaining + // time we have to render. So rendering one more row would likely + // exceed it. + now() * 2 - renderState.renderingStartTime > + getRenderTargetTime() && + renderLanes !== OffscreenLane + ) { + // We have now passed our CPU deadline and we'll just give up further + // attempts to render the main content and only render fallbacks. + // The assumption is that this is usually faster. + workInProgress.flags |= DidCapture; + didSuspendAlready = true; + cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this + // to get it started back up to attempt the next item. While in terms + // of priority this work has the same priority as this current render, + // it's not part of the same transition once the transition has + // committed. If it's sync, we still want to yield so that it can be + // painted. Conceptually, this is really the same as pinging. + // We can use any RetryLane even if it's the one currently rendering + // since we're leaving it behind on this node. - update.callback = function() { - onUncaughtError(error); - logCapturedError(fiber, errorInfo); - }; + workInProgress.lanes = SomeRetryLane; + } + } - return update; -} + if (renderState.isBackwards) { + // The effect list of the backwards tail will have been added + // to the end. This breaks the guarantee that life-cycles fire in + // sibling order but that isn't a strong guarantee promised by React. + // Especially since these might also just pop in during future commits. + // Append to the beginning of the list. + renderedTail.sibling = workInProgress.child; + workInProgress.child = renderedTail; + } else { + var previousSibling = renderState.last; -function createClassErrorUpdate(fiber, errorInfo, lane) { - var update = createUpdate(NoTimestamp, lane); - update.tag = CaptureUpdate; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if (previousSibling !== null) { + previousSibling.sibling = renderedTail; + } else { + workInProgress.child = renderedTail; + } - if (typeof getDerivedStateFromError === "function") { - var error$1 = errorInfo.value; + renderState.last = renderedTail; + } + } - update.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error$1); - }; - } + if (renderState.tail !== null) { + // We still have tail rows to render. + // Pop a row. + var next = renderState.tail; + renderState.rendering = next; + renderState.tail = next.sibling; + renderState.renderingStartTime = now(); + next.sibling = null; // Restore the context. + // TODO: We can probably just avoid popping it instead and only + // setting it the first time we go from not suspended to suspended. - var inst = fiber.stateNode; + var suspenseContext = suspenseStackCursor.current; - if (inst !== null && typeof inst.componentDidCatch === "function") { - update.callback = function callback() { - { - markFailedErrorBoundaryForHotReloading(fiber); - } + if (didSuspendAlready) { + suspenseContext = setShallowSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + } else { + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + } - if (typeof getDerivedStateFromError !== "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 getDerivedStateFromError is - // not defined. - markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined + pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. + // Don't bubble properties in this case. - logCapturedError(fiber, errorInfo); + return next; } - var error$1 = errorInfo.value; - var stack = errorInfo.stack; - this.componentDidCatch(error$1, { - componentStack: stack !== null ? stack : "" - }); + bubbleProperties(workInProgress); + return null; + } - { - if (typeof getDerivedStateFromError !== "function") { - // If componentDidCatch is the only error boundary method defined, - // then it needs to call setState to recover from errors. - // If no state update is scheduled then the boundary will swallow the error. - if (!includesSomeLane(fiber.lanes, SyncLane)) { - error( - "%s: Error boundaries should implement getDerivedStateFromError(). " + - "In that method, return a state update to display an error message or fallback UI.", - getComponentNameFromFiber(fiber) || "Unknown" - ); - } - } - } - }; - } else { - update.callback = function() { - markFailedErrorBoundaryForHotReloading(fiber); - }; - } + case ScopeComponent: { + break; + } - return update; -} + case OffscreenComponent: + case LegacyHiddenComponent: { + popRenderLanes(workInProgress); + var _nextState = workInProgress.memoizedState; + var nextIsHidden = _nextState !== null; -function attachPingListener(root, wakeable, lanes) { - // Attach a listener to the promise to "ping" the root and retry. But only if - // one does not already exist for the lanes we're currently rendering (which - // acts like a "thread ID" here). - var pingCache = root.pingCache; - var threadIDs; + if (current !== null) { + var _prevState = current.memoizedState; + var prevIsHidden = _prevState !== null; - if (pingCache === null) { - pingCache = root.pingCache = new PossiblyWeakMap$1(); - threadIDs = new Set(); - pingCache.set(wakeable, threadIDs); - } else { - threadIDs = pingCache.get(wakeable); + if ( + prevIsHidden !== nextIsHidden && + newProps.mode !== "unstable-defer-without-hiding" + ) { + workInProgress.flags |= Update; + } + } // Don't bubble properties for hidden children. - if (threadIDs === undefined) { - threadIDs = new Set(); - pingCache.set(wakeable, threadIDs); + if ( + !nextIsHidden || + includesSomeLane(subtreeRenderLanes, OffscreenLane) || + (workInProgress.mode & ConcurrentMode) === NoMode + ) { + bubbleProperties(workInProgress); + } + + return null; } } - if (!threadIDs.has(lanes)) { - // Memoize using the thread ID to prevent redundant listeners. - threadIDs.add(lanes); - var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); - - wakeable.then(ping, ping); + { + throw Error( + "Unknown unit of work tag (" + + workInProgress.tag + + "). This error is likely caused by a bug in React. Please file an issue." + ); } } -function throwException( - root, - returnFiber, - sourceFiber, - value, - rootRenderLanes -) { - // The source fiber did not complete. - sourceFiber.flags |= Incomplete; +function unwindWork(workInProgress, renderLanes) { + switch (workInProgress.tag) { + case ClassComponent: { + var Component = workInProgress.type; - if ( - value !== null && - typeof value === "object" && - typeof value.then === "function" - ) { - var wakeable = value; - // A legacy mode Suspense quirk, only relevant to hook components. + if (isContextProvider(Component)) { + popContext(workInProgress); + } - var tag = sourceFiber.tag; + var flags = workInProgress.flags; - if ( - (sourceFiber.mode & ConcurrentMode) === NoMode && - (tag === FunctionComponent || - tag === ForwardRef || - tag === SimpleMemoComponent) - ) { - var currentSource = sourceFiber.alternate; + if (flags & ShouldCapture) { + workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; - if (currentSource) { - sourceFiber.updateQueue = currentSource.updateQueue; - sourceFiber.memoizedState = currentSource.memoizedState; - sourceFiber.lanes = currentSource.lanes; - } else { - sourceFiber.updateQueue = null; - sourceFiber.memoizedState = null; + if ((workInProgress.mode & ProfileMode) !== NoMode) { + transferActualDuration(workInProgress); + } + + return workInProgress; } + + return null; } - var hasInvisibleParentBoundary = hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext - ); // Schedule the nearest Suspense to re-render the timed out view. + case HostRoot: { + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + resetWorkInProgressVersions(); + var _flags = workInProgress.flags; - var _workInProgress = returnFiber; + if (!((_flags & DidCapture) === NoFlags)) { + throw Error( + "The root failed to unmount after an error. This is likely a bug in React. Please file an issue." + ); + } - do { - if ( - _workInProgress.tag === SuspenseComponent && - shouldCaptureSuspense(_workInProgress, hasInvisibleParentBoundary) - ) { - // Found the nearest boundary. - // Stash the promise on the boundary fiber. If the boundary times out, we'll - // attach another listener to flip the boundary back to its normal state. - var wakeables = _workInProgress.updateQueue; + workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture; + return workInProgress; + } - if (wakeables === null) { - var updateQueue = new Set(); - updateQueue.add(wakeable); - _workInProgress.updateQueue = updateQueue; - } else { - wakeables.add(wakeable); - } // If the boundary is in legacy mode, we should *not* - // suspend the commit. Pretend as if the suspended component rendered - // null and keep rendering. In the commit phase, we'll schedule a - // subsequent synchronous update to re-render the Suspense. - // - // Note: It doesn't matter whether the component that suspended was - // inside a concurrent mode tree. If the Suspense is outside of it, we - // should *not* suspend the commit. - // - // If the suspense boundary suspended itself suspended, we don't have to - // do this trick because nothing was partially started. We can just - // directly do a second pass over the fallback in this render and - // pretend we meant to render that directly. + case HostComponent: { + // TODO: popHydrationState + popHostContext(workInProgress); + return null; + } - if ( - (_workInProgress.mode & ConcurrentMode) === NoMode && - _workInProgress !== returnFiber - ) { - _workInProgress.flags |= DidCapture; - sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. - // But we shouldn't call any lifecycle methods or callbacks. Remove - // all lifecycle effect tags. + case SuspenseComponent: { + popSuspenseContext(workInProgress); - sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); + var _flags2 = workInProgress.flags; - if (sourceFiber.tag === ClassComponent) { - var _currentSourceFiber = sourceFiber.alternate; + if (_flags2 & ShouldCapture) { + workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary. - if (_currentSourceFiber === null) { - // This is a new mount. Change the tag so it's not mistaken for a - // completed class component. For example, we should not call - // componentWillUnmount if it is deleted. - sourceFiber.tag = IncompleteClassComponent; - } else { - // When we try rendering again, we should not reuse the current fiber, - // since it's known to be in an inconsistent state. Use a force update to - // prevent a bail out. - var update = createUpdate(NoTimestamp, SyncLane); - update.tag = ForceUpdate; - enqueueUpdate(sourceFiber, update); - } - } // The source fiber did not complete. Mark it with Sync priority to - // indicate that it still has pending work. + if ((workInProgress.mode & ProfileMode) !== NoMode) { + transferActualDuration(workInProgress); + } - sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); // Exit without suspending. + return workInProgress; + } - return; - } // Confirmed that the boundary is in a concurrent mode tree. Continue - // with the normal suspend path. - // - // After this we'll use a set of heuristics to determine whether this - // render pass will run to completion or restart or "suspend" the commit. - // The actual logic for this is spread out in different places. - // - // This first principle is that if we're going to suspend when we complete - // a root, then we should also restart if we get an update or ping that - // might unsuspend it, and vice versa. The only reason to suspend is - // because you think you might want to restart before committing. However, - // it doesn't make sense to restart only while in the period we're suspended. - // - // Restarting too aggressively is also not good because it starves out any - // intermediate loading state. So we use heuristics to determine when. - // Suspense Heuristics - // - // If nothing threw a Promise or all the same fallbacks are already showing, - // then don't suspend/restart. - // - // If this is an initial render of a new tree of Suspense boundaries and - // those trigger a fallback, then don't suspend/restart. We want to ensure - // that we can show the initial loading state as quickly as possible. - // - // If we hit a "Delayed" case, such as when we'd switch from content back into - // a fallback, then we should always suspend/restart. Transitions apply - // to this case. If none is defined, JND is used instead. - // - // If we're already showing a fallback and it gets "retried", allowing us to show - // another level, but there's still an inner boundary that would show a fallback, - // then we suspend/restart for 500ms since the last time we showed a fallback - // anywhere in the tree. This effectively throttles progressive loading into a - // consistent train of commits. This also gives us an opportunity to restart to - // get to the completed state slightly earlier. - // - // If there's ambiguity due to batching it's resolved in preference of: - // 1) "delayed", 2) "initial render", 3) "retry". - // - // We want to ensure that a "busy" state doesn't get force committed. We want to - // ensure that new initial loading states can commit as soon as possible. + return null; + } - attachPingListener(root, wakeable, rootRenderLanes); - _workInProgress.flags |= ShouldCapture; - _workInProgress.lanes = rootRenderLanes; - return; - } // This boundary already captured during this render. Continue to the next - // boundary. + case SuspenseListComponent: { + popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been + // caught by a nested boundary. If not, it should bubble through. - _workInProgress = _workInProgress.return; - } while (_workInProgress !== null); // No boundary was found. Fallthrough to error mode. - // TODO: Use invariant so the message is stripped in prod? + return null; + } + + case HostPortal: + popHostContainer(workInProgress); + return null; + + case ContextProvider: + var context = workInProgress.type._context; + popProvider(context, workInProgress); + return null; - value = new Error( - (getComponentNameFromFiber(sourceFiber) || "A React component") + - " suspended while rendering, but no fallback UI was specified.\n" + - "\n" + - "Add a component higher in the tree to " + - "provide a loading indicator or placeholder to display." - ); - } // We didn't find a boundary that could handle this type of exception. Start - // over and traverse parent path again, this time treating the exception - // as an error. + case OffscreenComponent: + case LegacyHiddenComponent: + popRenderLanes(workInProgress); - renderDidError(); - value = createCapturedValue(value, sourceFiber); - var workInProgress = returnFiber; + return null; - do { - switch (workInProgress.tag) { - case HostRoot: { - var _errorInfo = value; - workInProgress.flags |= ShouldCapture; - var lane = pickArbitraryLane(rootRenderLanes); - workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); + case CacheComponent: + return null; - var _update = createRootErrorUpdate(workInProgress, _errorInfo, lane); + default: + return null; + } +} - enqueueCapturedUpdate(workInProgress, _update); - return; +function unwindInterruptedWork(interruptedWork, renderLanes) { + switch (interruptedWork.tag) { + case ClassComponent: { + var childContextTypes = interruptedWork.type.childContextTypes; + + if (childContextTypes !== null && childContextTypes !== undefined) { + popContext(interruptedWork); } - case ClassComponent: - // Capture and retry - var errorInfo = value; - var ctor = workInProgress.type; - var instance = workInProgress.stateNode; + break; + } - if ( - (workInProgress.flags & DidCapture) === NoFlags && - (typeof ctor.getDerivedStateFromError === "function" || - (instance !== null && - typeof instance.componentDidCatch === "function" && - !isAlreadyFailedLegacyErrorBoundary(instance))) - ) { - workInProgress.flags |= ShouldCapture; + case HostRoot: { + popHostContainer(interruptedWork); + popTopLevelContextObject(interruptedWork); + resetWorkInProgressVersions(); + break; + } - var _lane = pickArbitraryLane(rootRenderLanes); + case HostComponent: { + popHostContext(interruptedWork); + break; + } - workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state + case HostPortal: + popHostContainer(interruptedWork); + break; - var _update2 = createClassErrorUpdate( - workInProgress, - errorInfo, - _lane - ); + case SuspenseComponent: + popSuspenseContext(interruptedWork); + break; - enqueueCapturedUpdate(workInProgress, _update2); - return; - } + case SuspenseListComponent: + popSuspenseContext(interruptedWork); + break; - break; - } + case ContextProvider: + var context = interruptedWork.type._context; + popProvider(context, interruptedWork); + break; - workInProgress = workInProgress.return; - } while (workInProgress !== null); + case OffscreenComponent: + case LegacyHiddenComponent: + popRenderLanes(interruptedWork); + + break; + } } var didWarnAboutUndefinedSnapshotBeforeUpdate = null; @@ -16484,11 +16701,21 @@ var didWarnAboutUndefinedSnapshotBeforeUpdate = null; var PossiblyWeakSet = typeof WeakSet === "function" ? WeakSet : Set; var nextEffect = null; // Used for Profiling builds to track updaters. +var inProgressLanes = null; +var inProgressRoot = null; + var callComponentWillUnmountWithTimer = function(current, instance) { instance.props = current.memoizedProps; instance.state = current.memoizedState; - { + if (current.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentWillUnmount(); + } finally { + recordLayoutEffectDuration(current); + } + } else { instance.componentWillUnmount(); } }; // Capture errors so they don't interrupt mounting. @@ -16520,7 +16747,11 @@ function safelyDetachRef(current, nearestMountedAncestor) { if (ref !== null) { if (typeof ref === "function") { { - { + if (current.mode & ProfileMode) { + startLayoutEffectTimer(); + invokeGuardedCallback(null, ref, null, null); + recordLayoutEffectDuration(current); + } else { invokeGuardedCallback(null, ref, null, null); } @@ -16827,6 +17058,58 @@ function commitHookEffectListMount(tag, finishedWork) { } } +function commitPassiveEffectDurations(finishedRoot, finishedWork) { + { + // Only Profilers with work in their subtree will have an Update effect scheduled. + if ((finishedWork.flags & Update) !== NoFlags) { + switch (finishedWork.tag) { + case Profiler: { + var passiveEffectDuration = + finishedWork.stateNode.passiveEffectDuration; + var _finishedWork$memoize = finishedWork.memoizedProps, + id = _finishedWork$memoize.id, + onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase. + // It does not get reset until the start of the next commit phase. + + var commitTime = getCommitTime(); + var phase = finishedWork.alternate === null ? "mount" : "update"; + + { + if (isCurrentUpdateNested()) { + phase = "nested-update"; + } + } + + if (typeof onPostCommit === "function") { + onPostCommit(id, phase, passiveEffectDuration, commitTime); + } // Bubble times to the next nearest ancestor Profiler. + // After we process that Profiler, we'll bubble further up. + + var parentFiber = finishedWork.return; + + outer: while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.passiveEffectDuration += passiveEffectDuration; + break outer; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.passiveEffectDuration += passiveEffectDuration; + break outer; + } + + parentFiber = parentFiber.return; + } + + break; + } + } + } + } +} + function commitLayoutEffectOnFiber( finishedRoot, current, @@ -16842,7 +17125,14 @@ function commitLayoutEffectOnFiber( // This is done to prevent sibling component effects from interfering with each other, // e.g. a destroy function in one component should never override a ref set // by a create function in another component during the same commit. - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + commitHookEffectListMount(Layout | HasEffect, finishedWork); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { commitHookEffectListMount(Layout | HasEffect, finishedWork); } @@ -16886,7 +17176,14 @@ function commitLayoutEffectOnFiber( } } - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentDidMount(); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { instance.componentDidMount(); } } else { @@ -16927,7 +17224,18 @@ function commitLayoutEffectOnFiber( } } - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate + ); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { instance.componentDidUpdate( prevProps, prevState, @@ -17038,6 +17346,12 @@ function commitLayoutEffectOnFiber( var commitTime = getCommitTime(); var phase = current === null ? "mount" : "update"; + { + if (isCurrentUpdateNested()) { + phase = "nested-update"; + } + } + if (typeof onRender === "function") { onRender( finishedWork.memoizedProps.id, @@ -17048,6 +17362,40 @@ function commitLayoutEffectOnFiber( commitTime ); } + + { + if (typeof onCommit === "function") { + onCommit( + finishedWork.memoizedProps.id, + phase, + effectDuration, + commitTime + ); + } // Schedule a passive effect for this Profiler to call onPostCommit hooks. + // This effect should be scheduled even if there is no onPostCommit callback for this Profiler, + // because the effect is also where times bubble to parent Profilers. + + enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor. + // Do not reset these values until the next render so DevTools has a chance to read them first. + + var parentFiber = finishedWork.return; + + outer: while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.effectDuration += effectDuration; + break outer; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.effectDuration += effectDuration; + break outer; + } + + parentFiber = parentFiber.return; + } + } } break; @@ -17096,7 +17444,14 @@ function commitAttachRef(finishedWork) { } // Moved outside to ensure DCE works with this flag if (typeof ref === "function") { - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + ref(instanceToUse); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { ref(instanceToUse); } } else { @@ -17120,7 +17475,14 @@ function commitDetachRef(current) { if (currentRef !== null) { if (typeof currentRef === "function") { - { + if (current.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + currentRef(null); + } finally { + recordLayoutEffectDuration(current); + } + } else { currentRef(null); } } else { @@ -17155,7 +17517,11 @@ function commitUnmount(finishedRoot, current, nearestMountedAncestor) { if (destroy !== undefined) { if ((tag & Layout) !== NoFlags$1) { - { + if (current.mode & ProfileMode) { + startLayoutEffectTimer(); + safelyCallDestroy(current, nearestMountedAncestor, destroy); + recordLayoutEffectDuration(current); + } else { safelyCallDestroy(current, nearestMountedAncestor, destroy); } } @@ -17353,7 +17719,18 @@ function commitWork(current, finishedWork) { // This prevents sibling component effects from interfering with each other, // e.g. a destroy function in one component should never override a ref set // by a create function in another component during the same commit. - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + commitHookEffectListUnmount( + Layout | HasEffect, + finishedWork, + finishedWork.return + ); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { commitHookEffectListUnmount( Layout | HasEffect, finishedWork, @@ -17423,6 +17800,19 @@ function attachSuspenseRetryListeners(finishedWork) { if (!retryCache.has(wakeable)) { retryCache.add(wakeable); + { + if (isDevToolsPresent) { + if (inProgressLanes !== null && inProgressRoot !== null) { + // If we have pending work still, associate the original updaters with it. + restorePendingUpdaters(inProgressRoot, inProgressLanes); + } else { + throw Error( + "Expected finished root and lanes to be set. This is a bug in React." + ); + } + } + } + wakeable.then(retry, retry); } }); @@ -17445,8 +17835,12 @@ function isSuspenseBoundaryBeingHidden(current, finishedWork) { } function commitMutationEffects(root, firstChild, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; nextEffect = firstChild; commitMutationEffects_begin(root); + inProgressLanes = null; + inProgressRoot = null; } function commitMutationEffects_begin(root) { @@ -17580,8 +17974,12 @@ function commitMutationEffectsOnFiber(finishedWork, root) { } function commitLayoutEffects(finishedWork, root, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; nextEffect = finishedWork; commitLayoutEffects_begin(finishedWork, root, committedLanes); + inProgressLanes = null; + inProgressRoot = null; } function commitLayoutEffects_begin(subtreeRoot, root, committedLanes) { @@ -17714,7 +18112,15 @@ function commitPassiveMountOnFiber(finishedRoot, finishedWork) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (finishedWork.mode & ProfileMode) { + startPassiveEffectTimer(); + + try { + commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); + } finally { + recordPassiveEffectDuration(finishedWork); + } + } else { commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); } @@ -17815,7 +18221,15 @@ function commitPassiveUnmountOnFiber(finishedWork) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (finishedWork.mode & ProfileMode) { + startPassiveEffectTimer(); + commitHookEffectListUnmount( + Passive$1 | HasEffect, + finishedWork, + finishedWork.return + ); + recordPassiveEffectDuration(finishedWork); + } else { commitHookEffectListUnmount( Passive$1 | HasEffect, finishedWork, @@ -17889,7 +18303,11 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber( case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (current.mode & ProfileMode) { + startPassiveEffectTimer(); + commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); + recordPassiveEffectDuration(current); + } else { commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); } @@ -18017,6 +18435,7 @@ var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfi var rootDoesHavePassiveEffects = false; var rootWithPendingPassiveEffects = null; var pendingPassiveEffectsLanes = NoLanes; +var pendingPassiveProfilerEffects = []; // Use these to prevent an infinite loop of nested updates var NESTED_UPDATE_LIMIT = 50; var nestedUpdateCount = 0; @@ -18054,6 +18473,20 @@ function requestUpdateLane(fiber) { if ((mode & ConcurrentMode) === NoMode) { return SyncLane; + } else if ( + (executionContext & RenderContext) !== NoContext && + workInProgressRootRenderLanes !== NoLanes + ) { + // This is a render phase update. These are not officially supported. The + // old behavior is to give this the same "thread" (lanes) as + // whatever is currently rendering. So if you call `setState` on a component + // that happens later in the same render, it will flush. Ideally, we want to + // remove the special case and treat them as if they came from an + // interleaved event. Regardless, this pattern is not officially supported. + // This behavior is only a fallback. The flag only exists until we can roll + // out the setState warning, since existing code might accidentally rely on + // the current behavior. + return pickArbitraryLane(workInProgressRootRenderLanes); } var isTransition = requestCurrentTransition() !== NoTransition; @@ -18083,7 +18516,7 @@ function requestUpdateLane(fiber) { if (updateLane !== NoLane) { return updateLane; - } // This update originated outside React. Ask the host environement for an + } // This update originated outside React. Ask the host environment for an // appropriate priority, based on the type of event. // // The opaque type returned by the host config is internally a lane, so we can @@ -18118,6 +18551,12 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { return null; } + { + if (isDevToolsPresent) { + addFiberToLanesMap(root, fiber, lane); + } + } // Mark that the root has a pending update. + markRootUpdated(root, lane, eventTime); if (root === workInProgressRoot) { @@ -18126,7 +18565,7 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render // phase update. In that case, we don't treat render phase updates as if // they were interleaved, for backwards compat reasons. - { + if ((executionContext & RenderContext) === NoContext) { workInProgressRootUpdatedLanes = mergeLanes( workInProgressRootUpdatedLanes, lane @@ -18238,7 +18677,7 @@ function isInterleavedUpdate(fiber, lane) { // then don't treat this as an interleaved update. This pattern is // accompanied by a warning but we haven't fully deprecated it yet. We can // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled. - deferRenderPhaseUpdateToNextBatch + (executionContext & RenderContext) === NoContext ); } // Use this function to schedule a task for a root. There's only one task per // root; if a task was already scheduled, we'll check to make sure the priority @@ -18349,6 +18788,9 @@ function ensureRootIsScheduled(root, currentTime) { // goes through Scheduler. function performConcurrentWorkOnRoot(root, didTimeout) { + { + resetNestedUpdateFlag(); + } // Since we know we're in a React event, we can clear the current // event time. The next update will compute a new event time. currentEventTime = NoTimestamp; @@ -18371,7 +18813,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) { // there's a new task, or that there's no remaining work on this root. return null; } - } // Determine the next expiration time to work on, using the fields stored + } // Determine the next lanes to work on, using the fields stored // on the root. var lanes = getNextLanes( @@ -18576,6 +19018,10 @@ function markRootSuspended$1(root, suspendedLanes) { // through Scheduler function performSyncWorkOnRoot(root) { + { + syncNestedUpdateFlag(); + } + if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) { throw Error("Should not already be working."); } @@ -18876,6 +19322,22 @@ function renderRootSync(root, lanes) { // and prepare a fresh one. Otherwise we'll continue where we left off. if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + + if (memoizedUpdaters.size > 0) { + restorePendingUpdaters(root, workInProgressRootRenderLanes); + memoizedUpdaters.clear(); + } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. + // If we bailout on this work, we'll move them back (like above). + // It's important to move them now in case the work spawns more work at the same priority with different updaters. + // That way we can keep the current update and future updates separate. + + movePendingFibersToMemoized(root, lanes); + } + } + prepareFreshStack(root, lanes); } @@ -18922,6 +19384,22 @@ function renderRootConcurrent(root, lanes) { // and prepare a fresh one. Otherwise we'll continue where we left off. if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + + if (memoizedUpdaters.size > 0) { + restorePendingUpdaters(root, workInProgressRootRenderLanes); + memoizedUpdaters.clear(); + } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. + // If we bailout on this work, we'll move them back (like above). + // It's important to move them now in case the work spawns more work at the same priority with different updaters. + // That way we can keep the current update and future updates separate. + + movePendingFibersToMemoized(root, lanes); + } + } + resetRenderTimer(); prepareFreshStack(root, lanes); } @@ -19022,7 +19500,7 @@ function completeUnitOfWork(unitOfWork) { // 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(completedWork); // Because this fiber did not complete, don't reset its expiration time. + var _next = unwindWork(completedWork); // Because this fiber did not complete, don't reset its lanes. if (_next !== null) { // If completing this work spawned new work, do that next. We'll come @@ -19167,7 +19645,7 @@ function commitRootImpl(root, renderPriorityLevel) { } } // Check if there are any effects in the whole tree. // TODO: This is left over from the effect list implementation, where we had - // to check for the existence of `firstEffect` to satsify Flow. I think the + // to check for the existence of `firstEffect` to satisfy Flow. I think the // only other reason this optimization exists is because it affects profiling. // Reconsider whether this is necessary. @@ -19206,7 +19684,7 @@ function commitRootImpl(root, renderPriorityLevel) { recordCommitTime(); } - commitMutationEffects(root, finishedWork); + commitMutationEffects(root, finishedWork, lanes); resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after // the mutation phase, so that the previous tree is still current during @@ -19251,6 +19729,9 @@ function commitRootImpl(root, renderPriorityLevel) { } if (includesSomeLane(remainingLanes, SyncLane)) { + { + markNestedUpdateScheduled(); + } // Count the number of times the root synchronously re-renders without // finishing. If there are too many, it indicates an infinite update loop. if (root === rootWithNestedUpdates) { @@ -19264,6 +19745,12 @@ function commitRootImpl(root, renderPriorityLevel) { } onCommitRoot(finishedWork.stateNode, renderPriorityLevel); + + { + if (isDevToolsPresent) { + root.memoizedUpdaters.clear(); + } + } // additional work on this root is scheduled. ensureRootIsScheduled(root, now()); @@ -19327,6 +19814,19 @@ function flushPassiveEffects() { return false; } +function enqueuePendingPassiveProfilerEffect(fiber) { + { + pendingPassiveProfilerEffects.push(fiber); + + if (!rootDoesHavePassiveEffects) { + rootDoesHavePassiveEffects = true; + scheduleCallback(NormalPriority, function() { + flushPassiveEffects(); + return null; + }); + } + } +} function flushPassiveEffectsImpl() { if (rootWithPendingPassiveEffects === null) { @@ -19353,6 +19853,16 @@ function flushPassiveEffectsImpl() { commitPassiveUnmountEffects(root.current); commitPassiveMountEffects(root, root.current); // TODO: Move to commitPassiveMountEffects + { + var profilerEffects = pendingPassiveProfilerEffects; + pendingPassiveProfilerEffects = []; + + for (var i = 0; i < profilerEffects.length; i++) { + var _fiber = profilerEffects[i]; + commitPassiveEffectDurations(root, _fiber); + } + } + { isFlushingPassiveEffects = false; } @@ -19366,6 +19876,12 @@ function flushPassiveEffectsImpl() { onPostCommitRoot(root); + { + var stateNode = root.current.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; + } + return true; } @@ -19514,7 +20030,7 @@ function retryTimedOutBoundary(boundaryFiber, retryLane) { // The boundary fiber (a Suspense component or SuspenseList component) // previously was rendered in its fallback state. One of the promises that // suspended it has resolved, which means at least part of the tree was - // likely unblocked. Try rendering again, at a new expiration time. + // likely unblocked. Try rendering again, at a new lanes. if (retryLane === NoLane) { // TODO: Assign this to `suspenseState.retryLane`? to avoid // unnecessary entanglement? @@ -19875,6 +20391,18 @@ function warnAboutRenderPhaseUpdatesInDEV(fiber) { } } } // a 'shared' variable that changes when act() opens/closes in tests. +function restorePendingUpdaters(root, lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + memoizedUpdaters.forEach(function(schedulingFiber) { + addFiberToLanesMap(root, schedulingFiber, lanes); + }); // This function intentionally does not clear memoized updaters. + // Those may still be relevant to the current commit + // and a future one (e.g. Suspense). + } + } +} var didWarnAboutUnmockedScheduler = false; // TODO Before we release concurrent mode, revisit this and decide whether a mocked // scheduler is the actual recommendation. The alternative could be a testing build, @@ -20364,8 +20892,6 @@ var hasBadMapPolyfill; } } -var debugCounter = 1; - function FiberNode(tag, pendingProps, key, mode) { // Instance this.tag = tag; @@ -20421,7 +20947,6 @@ function FiberNode(tag, pendingProps, key, mode) { { // This isn't directly used but is handy for debugging internals: - this._debugID = debugCounter++; this._debugSource = null; this._debugOwner = null; this._debugNeedsRemount = false; @@ -20501,7 +21026,6 @@ function createWorkInProgress(current, pendingProps) { { // DEV-only fields - workInProgress._debugID = current._debugID; workInProgress._debugSource = current._debugSource; workInProgress._debugOwner = current._debugOwner; workInProgress._debugHookTypes = current._debugHookTypes; @@ -20709,7 +21233,8 @@ function createFiberFromTypeAndProps( case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - mode |= StrictLegacyMode | StrictEffectsMode; + mode |= StrictLegacyMode; + break; case REACT_PROFILER_TYPE: @@ -20951,7 +21476,6 @@ function assignFiberPropertiesInDEV(target, source) { target.treeBaseDuration = source.treeBaseDuration; } - target._debugID = source._debugID; target._debugSource = source._debugSource; target._debugOwner = source._debugOwner; target._debugNeedsRemount = source._debugNeedsRemount; @@ -20983,6 +21507,20 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.entangledLanes = NoLanes; this.entanglements = createLaneMap(NoLanes); + { + this.effectDuration = 0; + this.passiveEffectDuration = 0; + } + + { + this.memoizedUpdaters = new Set(); + var pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []); + + for (var i = 0; i < TotalLanes; i++) { + pendingUpdatersLaneMap.push(new Set()); + } + } + { switch (tag) { case ConcurrentRoot: @@ -21242,6 +21780,14 @@ function getPublicRootInstance(container) { } } +var shouldErrorImpl = function(fiber) { + return null; +}; + +function shouldError(fiber) { + return shouldErrorImpl(fiber); +} + var shouldSuspendImpl = function(fiber) { return false; }; @@ -21256,6 +21802,7 @@ var overrideProps = null; var overridePropsDeletePath = null; var overridePropsRenamePath = null; var scheduleUpdate = null; +var setErrorHandler = null; var setSuspenseHandler = null; { @@ -21443,6 +21990,10 @@ var setSuspenseHandler = null; scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp); }; + setErrorHandler = function(newShouldErrorImpl) { + shouldErrorImpl = newShouldErrorImpl; + }; + setSuspenseHandler = function(newShouldSuspendImpl) { shouldSuspendImpl = newShouldSuspendImpl; }; @@ -21480,6 +22031,7 @@ function injectIntoDevTools(devToolsConfig) { overrideProps: overrideProps, overridePropsDeletePath: overridePropsDeletePath, overridePropsRenamePath: overridePropsRenamePath, + setErrorHandler: setErrorHandler, setSuspenseHandler: setSuspenseHandler, scheduleUpdate: scheduleUpdate, currentDispatcherRef: ReactCurrentDispatcher, diff --git a/Libraries/Renderer/implementations/ReactFabric-prod.fb.js b/Libraries/Renderer/implementations/ReactFabric-prod.fb.js index 7d8cdbe0a52632..b399a6554ac10c 100644 --- a/Libraries/Renderer/implementations/ReactFabric-prod.fb.js +++ b/Libraries/Renderer/implementations/ReactFabric-prod.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<> + * @generated SignedSource<<683bd75bd1f5651f1e727c13530bd075>> */ "use strict"; @@ -2182,8 +2182,7 @@ function readContext(context) { lastContextDependency = context; currentlyRenderingFiber.dependencies = { lanes: 0, - firstContext: context, - responders: null + firstContext: context }; } else lastContextDependency = lastContextDependency.next = context; return value; @@ -2224,7 +2223,9 @@ function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; null !== updateQueue && ((updateQueue = updateQueue.shared), - null !== workInProgressRoot && 0 !== (fiber.mode & 1) + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) ? ((fiber = updateQueue.interleaved), null === fiber ? ((update.next = update), @@ -3782,7 +3783,11 @@ function dispatchAction(fiber, queue, action) { : ((update.next = lane.next), (lane.next = update)), (queue.pending = update); else { - if (null !== workInProgressRoot && 0 !== (fiber.mode & 1)) { + if ( + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) + ) { var interleaved = queue.interleaved; null === interleaved ? ((update.next = update), @@ -4019,8 +4024,82 @@ var ContextOnlyDispatcher = { return rerenderReducer(basicStateReducer)[0]; }, unstable_isNewReconciler: !1 - }, - ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + }; +function createCapturedValue(value, source) { + try { + var info = "", + node = source; + do (info += describeFiber(node)), (node = node.return); + while (node); + var JSCompiler_inline_result = info; + } catch (x) { + JSCompiler_inline_result = + "\nError generating stack: " + x.message + "\n" + x.stack; + } + return { value: value, source: source, stack: JSCompiler_inline_result }; +} +if ( + "function" !== + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog +) + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +function logCapturedError(boundary, errorInfo) { + try { + !1 !== + ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ + componentStack: null !== errorInfo.stack ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + null !== boundary && 1 === boundary.tag ? boundary.stateNode : null + }) && console.error(errorInfo.value); + } catch (e) { + setTimeout(function() { + throw e; + }); + } +} +var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; +function createRootErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + lane.payload = { element: null }; + var error = errorInfo.value; + lane.callback = function() { + hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); + logCapturedError(fiber, errorInfo); + }; + return lane; +} +function createClassErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if ("function" === typeof getDerivedStateFromError) { + var error = errorInfo.value; + lane.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error); + }; + } + var inst = fiber.stateNode; + null !== inst && + "function" === typeof inst.componentDidCatch && + (lane.callback = function() { + "function" !== typeof getDerivedStateFromError && + (null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) + : legacyErrorBoundariesThatAlreadyFailed.add(this), + logCapturedError(fiber, errorInfo)); + var stack = errorInfo.stack; + this.componentDidCatch(errorInfo.value, { + componentStack: null !== stack ? stack : "" + }); + }); + return lane; +} +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -5038,14 +5117,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$64 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$64 = lastTailNode), + for (var lastTailNode$69 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$69 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$64 + null === lastTailNode$69 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$64.sibling = null); + : (lastTailNode$69.sibling = null); } } function bubbleProperties(completedWork) { @@ -5055,19 +5134,19 @@ function bubbleProperties(completedWork) { newChildLanes = 0, subtreeFlags = 0; if (didBailout) - for (var child$65 = completedWork.child; null !== child$65; ) - (newChildLanes |= child$65.lanes | child$65.childLanes), - (subtreeFlags |= child$65.subtreeFlags & 1835008), - (subtreeFlags |= child$65.flags & 1835008), - (child$65.return = completedWork), - (child$65 = child$65.sibling); + for (var child$70 = completedWork.child; null !== child$70; ) + (newChildLanes |= child$70.lanes | child$70.childLanes), + (subtreeFlags |= child$70.subtreeFlags & 1835008), + (subtreeFlags |= child$70.flags & 1835008), + (child$70.return = completedWork), + (child$70 = child$70.sibling); else - for (child$65 = completedWork.child; null !== child$65; ) - (newChildLanes |= child$65.lanes | child$65.childLanes), - (subtreeFlags |= child$65.subtreeFlags), - (subtreeFlags |= child$65.flags), - (child$65.return = completedWork), - (child$65 = child$65.sibling); + for (child$70 = completedWork.child; null !== child$70; ) + (newChildLanes |= child$70.lanes | child$70.childLanes), + (subtreeFlags |= child$70.subtreeFlags), + (subtreeFlags |= child$70.flags), + (child$70.return = completedWork), + (child$70 = child$70.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -5426,80 +5505,6 @@ function unwindWork(workInProgress) { return null; } } -function createCapturedValue(value, source) { - try { - var info = "", - node = source; - do (info += describeFiber(node)), (node = node.return); - while (node); - var JSCompiler_inline_result = info; - } catch (x) { - JSCompiler_inline_result = - "\nError generating stack: " + x.message + "\n" + x.stack; - } - return { value: value, source: source, stack: JSCompiler_inline_result }; -} -if ( - "function" !== - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog -) - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -function logCapturedError(boundary, errorInfo) { - try { - !1 !== - ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ - componentStack: null !== errorInfo.stack ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - null !== boundary && 1 === boundary.tag ? boundary.stateNode : null - }) && console.error(errorInfo.value); - } catch (e) { - setTimeout(function() { - throw e; - }); - } -} -var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; -function createRootErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - lane.payload = { element: null }; - var error = errorInfo.value; - lane.callback = function() { - hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); - logCapturedError(fiber, errorInfo); - }; - return lane; -} -function createClassErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ("function" === typeof getDerivedStateFromError) { - var error = errorInfo.value; - lane.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error); - }; - } - var inst = fiber.stateNode; - null !== inst && - "function" === typeof inst.componentDidCatch && - (lane.callback = function() { - "function" !== typeof getDerivedStateFromError && - (null === legacyErrorBoundariesThatAlreadyFailed - ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) - : legacyErrorBoundariesThatAlreadyFailed.add(this), - logCapturedError(fiber, errorInfo)); - var stack = errorInfo.stack; - this.componentDidCatch(errorInfo.value, { - componentStack: null !== stack ? stack : "" - }); - }); - return lane; -} var PossiblyWeakSet = "function" === typeof WeakSet ? WeakSet : Set, nextEffect = null; function safelyDetachRef(current, nearestMountedAncestor) { @@ -5998,6 +6003,8 @@ function requestEventTime() { } function requestUpdateLane(fiber) { if (0 === (fiber.mode & 1)) return 1; + if (0 !== (executionContext & 8) && 0 !== workInProgressRootRenderLanes) + return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; if (0 !== ReactCurrentBatchConfig.transition) return ( 0 === currentEventTransitionLane && @@ -6021,7 +6028,7 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { if (null === root) return null; markRootUpdated(root, lane, eventTime); root === workInProgressRoot && - ((workInProgressRootUpdatedLanes |= lane), + (0 === (executionContext & 8) && (workInProgressRootUpdatedLanes |= lane), 4 === workInProgressRootExitStatus && markRootSuspended$1(root, workInProgressRootRenderLanes)); 1 === lane @@ -6427,15 +6434,15 @@ function handleError(root$jscomp$0, thrownValue) { } var hasInvisibleParentBoundary = 0 !== (suspenseStackCursor.current & 1), - workInProgress$77 = returnFiber; + workInProgress$30 = returnFiber; do { var JSCompiler_temp; - if ((JSCompiler_temp = 13 === workInProgress$77.tag)) { - var nextState = workInProgress$77.memoizedState; + if ((JSCompiler_temp = 13 === workInProgress$30.tag)) { + var nextState = workInProgress$30.memoizedState; if (null !== nextState) JSCompiler_temp = null !== nextState.dehydrated ? !0 : !1; else { - var props = workInProgress$77.memoizedProps; + var props = workInProgress$30.memoizedProps; JSCompiler_temp = void 0 === props.fallback ? !1 @@ -6447,17 +6454,17 @@ function handleError(root$jscomp$0, thrownValue) { } } if (JSCompiler_temp) { - var wakeables = workInProgress$77.updateQueue; + var wakeables = workInProgress$30.updateQueue; if (null === wakeables) { var updateQueue = new Set(); updateQueue.add(wakeable); - workInProgress$77.updateQueue = updateQueue; + workInProgress$30.updateQueue = updateQueue; } else wakeables.add(wakeable); if ( - 0 === (workInProgress$77.mode & 1) && - workInProgress$77 !== returnFiber + 0 === (workInProgress$30.mode & 1) && + workInProgress$30 !== returnFiber ) { - workInProgress$77.flags |= 128; + workInProgress$30.flags |= 128; sourceFiber.flags |= 32768; sourceFiber.flags &= -10053; if (1 === sourceFiber.tag) @@ -6490,12 +6497,12 @@ function handleError(root$jscomp$0, thrownValue) { ); wakeable.then(ping, ping); } - workInProgress$77.flags |= 16384; - workInProgress$77.lanes = thrownValue; + workInProgress$30.flags |= 16384; + workInProgress$30.lanes = thrownValue; break a; } - workInProgress$77 = workInProgress$77.return; - } while (null !== workInProgress$77); + workInProgress$30 = workInProgress$30.return; + } while (null !== workInProgress$30); value = Error( (getComponentNameFromFiber(sourceFiber) || "A React component") + " suspended while rendering, but no fallback UI was specified.\n\nAdd a component higher in the tree to provide a loading indicator or placeholder to display." @@ -6504,47 +6511,47 @@ function handleError(root$jscomp$0, thrownValue) { 5 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2); value = createCapturedValue(value, sourceFiber); - workInProgress$77 = returnFiber; + workInProgress$30 = returnFiber; do { - switch (workInProgress$77.tag) { + switch (workInProgress$30.tag) { case 3: root = value; - workInProgress$77.flags |= 16384; + workInProgress$30.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$77.lanes |= thrownValue; - var update$78 = createRootErrorUpdate( - workInProgress$77, + workInProgress$30.lanes |= thrownValue; + var update$31 = createRootErrorUpdate( + workInProgress$30, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$77, update$78); + enqueueCapturedUpdate(workInProgress$30, update$31); break a; case 1: root = value; - var ctor = workInProgress$77.type, - instance = workInProgress$77.stateNode; + var ctor = workInProgress$30.type, + instance = workInProgress$30.stateNode; if ( - 0 === (workInProgress$77.flags & 128) && + 0 === (workInProgress$30.flags & 128) && ("function" === typeof ctor.getDerivedStateFromError || (null !== instance && "function" === typeof instance.componentDidCatch && (null === legacyErrorBoundariesThatAlreadyFailed || !legacyErrorBoundariesThatAlreadyFailed.has(instance)))) ) { - workInProgress$77.flags |= 16384; + workInProgress$30.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$77.lanes |= thrownValue; - var update$81 = createClassErrorUpdate( - workInProgress$77, + workInProgress$30.lanes |= thrownValue; + var update$34 = createClassErrorUpdate( + workInProgress$30, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$77, update$81); + enqueueCapturedUpdate(workInProgress$30, update$34); break a; } } - workInProgress$77 = workInProgress$77.return; - } while (null !== workInProgress$77); + workInProgress$30 = workInProgress$30.return; + } while (null !== workInProgress$30); } completeUnitOfWork(erroredWork); } catch (yetAnotherThrownValue) { @@ -7547,7 +7554,7 @@ function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = 8; - mode |= 24; + mode |= 8; break; case REACT_PROFILER_TYPE: return ( @@ -7772,7 +7779,7 @@ var roots = new Map(), devToolsConfig$jscomp$inline_942 = { findFiberByHostInstance: getInstanceFromInstance, bundleType: 0, - version: "17.0.3-2d8d133e1", + version: "17.0.3-0eea57724", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function() { @@ -7798,6 +7805,7 @@ var internals$jscomp$inline_1180 = { overrideProps: null, overridePropsDeletePath: null, overridePropsRenamePath: null, + setErrorHandler: null, setSuspenseHandler: null, scheduleUpdate: null, currentDispatcherRef: ReactSharedInternals.ReactCurrentDispatcher, @@ -7813,7 +7821,7 @@ var internals$jscomp$inline_1180 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "17.0.3-2d8d133e1" + reconcilerVersion: "17.0.3-0eea57724" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1181 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/Libraries/Renderer/implementations/ReactFabric-prod.js b/Libraries/Renderer/implementations/ReactFabric-prod.js index ffd5e16d2974ce..fe20ba5470f848 100644 --- a/Libraries/Renderer/implementations/ReactFabric-prod.js +++ b/Libraries/Renderer/implementations/ReactFabric-prod.js @@ -8,7 +8,7 @@ * @nolint * @providesModule ReactFabric-prod * @preventMunge - * @generated SignedSource<> + * @generated SignedSource<> */ "use strict"; @@ -2181,8 +2181,7 @@ function readContext(context) { lastContextDependency = context; currentlyRenderingFiber.dependencies = { lanes: 0, - firstContext: context, - responders: null + firstContext: context }; } else lastContextDependency = lastContextDependency.next = context; return value; @@ -2223,7 +2222,9 @@ function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; null !== updateQueue && ((updateQueue = updateQueue.shared), - null !== workInProgressRoot && 0 !== (fiber.mode & 1) + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) ? ((fiber = updateQueue.interleaved), null === fiber ? ((update.next = update), @@ -3781,7 +3782,11 @@ function dispatchAction(fiber, queue, action) { : ((update.next = lane.next), (lane.next = update)), (queue.pending = update); else { - if (null !== workInProgressRoot && 0 !== (fiber.mode & 1)) { + if ( + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) + ) { var interleaved = queue.interleaved; null === interleaved ? ((update.next = update), @@ -4018,8 +4023,82 @@ var ContextOnlyDispatcher = { return rerenderReducer(basicStateReducer)[0]; }, unstable_isNewReconciler: !1 - }, - ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + }; +function createCapturedValue(value, source) { + try { + var info = "", + node = source; + do (info += describeFiber(node)), (node = node.return); + while (node); + var JSCompiler_inline_result = info; + } catch (x) { + JSCompiler_inline_result = + "\nError generating stack: " + x.message + "\n" + x.stack; + } + return { value: value, source: source, stack: JSCompiler_inline_result }; +} +if ( + "function" !== + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog +) + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +function logCapturedError(boundary, errorInfo) { + try { + !1 !== + ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ + componentStack: null !== errorInfo.stack ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + null !== boundary && 1 === boundary.tag ? boundary.stateNode : null + }) && console.error(errorInfo.value); + } catch (e) { + setTimeout(function() { + throw e; + }); + } +} +var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; +function createRootErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + lane.payload = { element: null }; + var error = errorInfo.value; + lane.callback = function() { + hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); + logCapturedError(fiber, errorInfo); + }; + return lane; +} +function createClassErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if ("function" === typeof getDerivedStateFromError) { + var error = errorInfo.value; + lane.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error); + }; + } + var inst = fiber.stateNode; + null !== inst && + "function" === typeof inst.componentDidCatch && + (lane.callback = function() { + "function" !== typeof getDerivedStateFromError && + (null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) + : legacyErrorBoundariesThatAlreadyFailed.add(this), + logCapturedError(fiber, errorInfo)); + var stack = errorInfo.stack; + this.componentDidCatch(errorInfo.value, { + componentStack: null !== stack ? stack : "" + }); + }); + return lane; +} +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -5037,14 +5116,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$64 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$64 = lastTailNode), + for (var lastTailNode$69 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$69 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$64 + null === lastTailNode$69 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$64.sibling = null); + : (lastTailNode$69.sibling = null); } } function bubbleProperties(completedWork) { @@ -5054,19 +5133,19 @@ function bubbleProperties(completedWork) { newChildLanes = 0, subtreeFlags = 0; if (didBailout) - for (var child$65 = completedWork.child; null !== child$65; ) - (newChildLanes |= child$65.lanes | child$65.childLanes), - (subtreeFlags |= child$65.subtreeFlags & 1835008), - (subtreeFlags |= child$65.flags & 1835008), - (child$65.return = completedWork), - (child$65 = child$65.sibling); + for (var child$70 = completedWork.child; null !== child$70; ) + (newChildLanes |= child$70.lanes | child$70.childLanes), + (subtreeFlags |= child$70.subtreeFlags & 1835008), + (subtreeFlags |= child$70.flags & 1835008), + (child$70.return = completedWork), + (child$70 = child$70.sibling); else - for (child$65 = completedWork.child; null !== child$65; ) - (newChildLanes |= child$65.lanes | child$65.childLanes), - (subtreeFlags |= child$65.subtreeFlags), - (subtreeFlags |= child$65.flags), - (child$65.return = completedWork), - (child$65 = child$65.sibling); + for (child$70 = completedWork.child; null !== child$70; ) + (newChildLanes |= child$70.lanes | child$70.childLanes), + (subtreeFlags |= child$70.subtreeFlags), + (subtreeFlags |= child$70.flags), + (child$70.return = completedWork), + (child$70 = child$70.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -5425,80 +5504,6 @@ function unwindWork(workInProgress) { return null; } } -function createCapturedValue(value, source) { - try { - var info = "", - node = source; - do (info += describeFiber(node)), (node = node.return); - while (node); - var JSCompiler_inline_result = info; - } catch (x) { - JSCompiler_inline_result = - "\nError generating stack: " + x.message + "\n" + x.stack; - } - return { value: value, source: source, stack: JSCompiler_inline_result }; -} -if ( - "function" !== - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog -) - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -function logCapturedError(boundary, errorInfo) { - try { - !1 !== - ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ - componentStack: null !== errorInfo.stack ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - null !== boundary && 1 === boundary.tag ? boundary.stateNode : null - }) && console.error(errorInfo.value); - } catch (e) { - setTimeout(function() { - throw e; - }); - } -} -var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; -function createRootErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - lane.payload = { element: null }; - var error = errorInfo.value; - lane.callback = function() { - hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); - logCapturedError(fiber, errorInfo); - }; - return lane; -} -function createClassErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ("function" === typeof getDerivedStateFromError) { - var error = errorInfo.value; - lane.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error); - }; - } - var inst = fiber.stateNode; - null !== inst && - "function" === typeof inst.componentDidCatch && - (lane.callback = function() { - "function" !== typeof getDerivedStateFromError && - (null === legacyErrorBoundariesThatAlreadyFailed - ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) - : legacyErrorBoundariesThatAlreadyFailed.add(this), - logCapturedError(fiber, errorInfo)); - var stack = errorInfo.stack; - this.componentDidCatch(errorInfo.value, { - componentStack: null !== stack ? stack : "" - }); - }); - return lane; -} var PossiblyWeakSet = "function" === typeof WeakSet ? WeakSet : Set, nextEffect = null; function safelyDetachRef(current, nearestMountedAncestor) { @@ -5997,6 +6002,8 @@ function requestEventTime() { } function requestUpdateLane(fiber) { if (0 === (fiber.mode & 1)) return 1; + if (0 !== (executionContext & 8) && 0 !== workInProgressRootRenderLanes) + return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; if (0 !== ReactCurrentBatchConfig.transition) return ( 0 === currentEventTransitionLane && @@ -6020,7 +6027,7 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { if (null === root) return null; markRootUpdated(root, lane, eventTime); root === workInProgressRoot && - ((workInProgressRootUpdatedLanes |= lane), + (0 === (executionContext & 8) && (workInProgressRootUpdatedLanes |= lane), 4 === workInProgressRootExitStatus && markRootSuspended$1(root, workInProgressRootRenderLanes)); 1 === lane @@ -6422,15 +6429,15 @@ function handleError(root$jscomp$0, thrownValue) { } var hasInvisibleParentBoundary = 0 !== (suspenseStackCursor.current & 1), - workInProgress$77 = returnFiber; + workInProgress$30 = returnFiber; do { var JSCompiler_temp; - if ((JSCompiler_temp = 13 === workInProgress$77.tag)) { - var nextState = workInProgress$77.memoizedState; + if ((JSCompiler_temp = 13 === workInProgress$30.tag)) { + var nextState = workInProgress$30.memoizedState; if (null !== nextState) JSCompiler_temp = null !== nextState.dehydrated ? !0 : !1; else { - var props = workInProgress$77.memoizedProps; + var props = workInProgress$30.memoizedProps; JSCompiler_temp = void 0 === props.fallback ? !1 @@ -6442,17 +6449,17 @@ function handleError(root$jscomp$0, thrownValue) { } } if (JSCompiler_temp) { - var wakeables = workInProgress$77.updateQueue; + var wakeables = workInProgress$30.updateQueue; if (null === wakeables) { var updateQueue = new Set(); updateQueue.add(wakeable); - workInProgress$77.updateQueue = updateQueue; + workInProgress$30.updateQueue = updateQueue; } else wakeables.add(wakeable); if ( - 0 === (workInProgress$77.mode & 1) && - workInProgress$77 !== returnFiber + 0 === (workInProgress$30.mode & 1) && + workInProgress$30 !== returnFiber ) { - workInProgress$77.flags |= 128; + workInProgress$30.flags |= 128; sourceFiber.flags |= 32768; sourceFiber.flags &= -10053; if (1 === sourceFiber.tag) @@ -6485,12 +6492,12 @@ function handleError(root$jscomp$0, thrownValue) { ); wakeable.then(ping, ping); } - workInProgress$77.flags |= 16384; - workInProgress$77.lanes = thrownValue; + workInProgress$30.flags |= 16384; + workInProgress$30.lanes = thrownValue; break a; } - workInProgress$77 = workInProgress$77.return; - } while (null !== workInProgress$77); + workInProgress$30 = workInProgress$30.return; + } while (null !== workInProgress$30); value = Error( (getComponentNameFromFiber(sourceFiber) || "A React component") + " suspended while rendering, but no fallback UI was specified.\n\nAdd a component higher in the tree to provide a loading indicator or placeholder to display." @@ -6499,47 +6506,47 @@ function handleError(root$jscomp$0, thrownValue) { 5 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2); value = createCapturedValue(value, sourceFiber); - workInProgress$77 = returnFiber; + workInProgress$30 = returnFiber; do { - switch (workInProgress$77.tag) { + switch (workInProgress$30.tag) { case 3: root = value; - workInProgress$77.flags |= 16384; + workInProgress$30.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$77.lanes |= thrownValue; - var update$78 = createRootErrorUpdate( - workInProgress$77, + workInProgress$30.lanes |= thrownValue; + var update$31 = createRootErrorUpdate( + workInProgress$30, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$77, update$78); + enqueueCapturedUpdate(workInProgress$30, update$31); break a; case 1: root = value; - var ctor = workInProgress$77.type, - instance = workInProgress$77.stateNode; + var ctor = workInProgress$30.type, + instance = workInProgress$30.stateNode; if ( - 0 === (workInProgress$77.flags & 128) && + 0 === (workInProgress$30.flags & 128) && ("function" === typeof ctor.getDerivedStateFromError || (null !== instance && "function" === typeof instance.componentDidCatch && (null === legacyErrorBoundariesThatAlreadyFailed || !legacyErrorBoundariesThatAlreadyFailed.has(instance)))) ) { - workInProgress$77.flags |= 16384; + workInProgress$30.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$77.lanes |= thrownValue; - var update$81 = createClassErrorUpdate( - workInProgress$77, + workInProgress$30.lanes |= thrownValue; + var update$34 = createClassErrorUpdate( + workInProgress$30, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$77, update$81); + enqueueCapturedUpdate(workInProgress$30, update$34); break a; } } - workInProgress$77 = workInProgress$77.return; - } while (null !== workInProgress$77); + workInProgress$30 = workInProgress$30.return; + } while (null !== workInProgress$30); } completeUnitOfWork(erroredWork); } catch (yetAnotherThrownValue) { @@ -7542,7 +7549,7 @@ function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = 8; - mode |= 24; + mode |= 8; break; case REACT_PROFILER_TYPE: return ( @@ -7767,7 +7774,7 @@ var roots = new Map(), devToolsConfig$jscomp$inline_942 = { findFiberByHostInstance: getInstanceFromInstance, bundleType: 0, - version: "17.0.3-experimental-2d8d133e1", + version: "17.0.3-experimental-0eea57724", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function() { @@ -7793,6 +7800,7 @@ var internals$jscomp$inline_1180 = { overrideProps: null, overridePropsDeletePath: null, overridePropsRenamePath: null, + setErrorHandler: null, setSuspenseHandler: null, scheduleUpdate: null, currentDispatcherRef: ReactSharedInternals.ReactCurrentDispatcher, @@ -7808,7 +7816,7 @@ var internals$jscomp$inline_1180 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "17.0.3-experimental-2d8d133e1" + reconcilerVersion: "17.0.3-experimental-0eea57724" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1181 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js b/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js index cbf24e776743ba..2ed158b285af14 100644 --- a/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js +++ b/Libraries/Renderer/implementations/ReactFabric-profiling.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<<08382e61f6bfa261be904923b08bfce7>> + * @generated SignedSource<<21f28e209cfb07c744f2addfe335ba8b>> */ "use strict"; @@ -930,7 +930,7 @@ eventPluginOrder = Array.prototype.slice.call([ "ReactNativeBridgeEventPlugin" ]); recomputePluginOrdering(); -var injectedNamesToPlugins$jscomp$inline_221 = { +var injectedNamesToPlugins$jscomp$inline_224 = { ResponderEventPlugin: ResponderEventPlugin, ReactNativeBridgeEventPlugin: { eventTypes: {}, @@ -965,34 +965,34 @@ var injectedNamesToPlugins$jscomp$inline_221 = { } } }, - isOrderingDirty$jscomp$inline_222 = !1, - pluginName$jscomp$inline_223; -for (pluginName$jscomp$inline_223 in injectedNamesToPlugins$jscomp$inline_221) + isOrderingDirty$jscomp$inline_225 = !1, + pluginName$jscomp$inline_226; +for (pluginName$jscomp$inline_226 in injectedNamesToPlugins$jscomp$inline_224) if ( - injectedNamesToPlugins$jscomp$inline_221.hasOwnProperty( - pluginName$jscomp$inline_223 + injectedNamesToPlugins$jscomp$inline_224.hasOwnProperty( + pluginName$jscomp$inline_226 ) ) { - var pluginModule$jscomp$inline_224 = - injectedNamesToPlugins$jscomp$inline_221[pluginName$jscomp$inline_223]; + var pluginModule$jscomp$inline_227 = + injectedNamesToPlugins$jscomp$inline_224[pluginName$jscomp$inline_226]; if ( - !namesToPlugins.hasOwnProperty(pluginName$jscomp$inline_223) || - namesToPlugins[pluginName$jscomp$inline_223] !== - pluginModule$jscomp$inline_224 + !namesToPlugins.hasOwnProperty(pluginName$jscomp$inline_226) || + namesToPlugins[pluginName$jscomp$inline_226] !== + pluginModule$jscomp$inline_227 ) { - if (namesToPlugins[pluginName$jscomp$inline_223]) + if (namesToPlugins[pluginName$jscomp$inline_226]) throw Error( "EventPluginRegistry: Cannot inject two different event plugins using the same name, `" + - pluginName$jscomp$inline_223 + + pluginName$jscomp$inline_226 + "`." ); namesToPlugins[ - pluginName$jscomp$inline_223 - ] = pluginModule$jscomp$inline_224; - isOrderingDirty$jscomp$inline_222 = !0; + pluginName$jscomp$inline_226 + ] = pluginModule$jscomp$inline_227; + isOrderingDirty$jscomp$inline_225 = !0; } } -isOrderingDirty$jscomp$inline_222 && recomputePluginOrdering(); +isOrderingDirty$jscomp$inline_225 && recomputePluginOrdering(); function getInstanceFromInstance(instanceHandle) { return instanceHandle; } @@ -1831,6 +1831,36 @@ function markRootEntangled(root, entangledLanes) { rootEntangledLanes &= ~lane; } } +function addFiberToLanesMap(root, fiber, lanes) { + if (isDevToolsPresent) + for (root = root.pendingUpdatersLaneMap; 0 < lanes; ) { + var index$9 = 31 - clz32(lanes), + lane = 1 << index$9; + root[index$9].add(fiber); + lanes &= ~lane; + } +} +function movePendingFibersToMemoized(root, lanes) { + if (isDevToolsPresent) + for ( + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap, + memoizedUpdaters = root.memoizedUpdaters; + 0 < lanes; + + ) { + var index$10 = 31 - clz32(lanes); + root = 1 << index$10; + index$10 = pendingUpdatersLaneMap[index$10]; + 0 < index$10.size && + (index$10.forEach(function(fiber) { + var alternate = fiber.alternate; + (null !== alternate && memoizedUpdaters.has(alternate)) || + memoizedUpdaters.add(fiber); + }), + index$10.clear()); + lanes &= ~root; + } +} var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log = Math.log, LN2 = Math.LN2; @@ -2200,8 +2230,7 @@ function readContext(context) { lastContextDependency = context; currentlyRenderingFiber.dependencies = { lanes: 0, - firstContext: context, - responders: null + firstContext: context }; } else lastContextDependency = lastContextDependency.next = context; return value; @@ -2242,7 +2271,9 @@ function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; null !== updateQueue && ((updateQueue = updateQueue.shared), - null !== workInProgressRoot && 0 !== (fiber.mode & 1) + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) ? ((fiber = updateQueue.interleaved), null === fiber ? ((update.next = update), @@ -3800,7 +3831,11 @@ function dispatchAction(fiber, queue, action) { : ((update.next = lane.next), (lane.next = update)), (queue.pending = update); else { - if (null !== workInProgressRoot && 0 !== (fiber.mode & 1)) { + if ( + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) + ) { var interleaved = queue.interleaved; null === interleaved ? ((update.next = update), @@ -4040,7 +4075,11 @@ var ContextOnlyDispatcher = { }, now$1 = Scheduler.unstable_now, commitTime = 0, - profilerStartTime = -1; + layoutEffectStartTime = -1, + profilerStartTime = -1, + passiveEffectStartTime = -1, + currentUpdateIsNested = !1, + nestedUpdateScheduled = !1; function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { if (0 <= profilerStartTime) { var elapsedTime = now$1() - profilerStartTime; @@ -4049,10 +4088,123 @@ function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { profilerStartTime = -1; } } +function recordLayoutEffectDuration(fiber) { + if (0 <= layoutEffectStartTime) { + var elapsedTime = now$1() - layoutEffectStartTime; + layoutEffectStartTime = -1; + for (fiber = fiber.return; null !== fiber; ) { + switch (fiber.tag) { + case 3: + fiber.stateNode.effectDuration += elapsedTime; + return; + case 12: + fiber.stateNode.effectDuration += elapsedTime; + return; + } + fiber = fiber.return; + } + } +} +function recordPassiveEffectDuration(fiber) { + if (0 <= passiveEffectStartTime) { + var elapsedTime = now$1() - passiveEffectStartTime; + passiveEffectStartTime = -1; + for (fiber = fiber.return; null !== fiber; ) { + switch (fiber.tag) { + case 3: + fiber = fiber.stateNode; + null !== fiber && (fiber.passiveEffectDuration += elapsedTime); + return; + case 12: + fiber = fiber.stateNode; + null !== fiber && (fiber.passiveEffectDuration += elapsedTime); + return; + } + fiber = fiber.return; + } + } +} +function startLayoutEffectTimer() { + layoutEffectStartTime = now$1(); +} function transferActualDuration(fiber) { for (var child = fiber.child; child; ) (fiber.actualDuration += child.actualDuration), (child = child.sibling); } +function createCapturedValue(value, source) { + try { + var info = "", + node = source; + do (info += describeFiber(node)), (node = node.return); + while (node); + var JSCompiler_inline_result = info; + } catch (x) { + JSCompiler_inline_result = + "\nError generating stack: " + x.message + "\n" + x.stack; + } + return { value: value, source: source, stack: JSCompiler_inline_result }; +} +if ( + "function" !== + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog +) + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +function logCapturedError(boundary, errorInfo) { + try { + !1 !== + ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ + componentStack: null !== errorInfo.stack ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + null !== boundary && 1 === boundary.tag ? boundary.stateNode : null + }) && console.error(errorInfo.value); + } catch (e) { + setTimeout(function() { + throw e; + }); + } +} +var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; +function createRootErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + lane.payload = { element: null }; + var error = errorInfo.value; + lane.callback = function() { + hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); + logCapturedError(fiber, errorInfo); + }; + return lane; +} +function createClassErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if ("function" === typeof getDerivedStateFromError) { + var error = errorInfo.value; + lane.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error); + }; + } + var inst = fiber.stateNode; + null !== inst && + "function" === typeof inst.componentDidCatch && + (lane.callback = function() { + "function" !== typeof getDerivedStateFromError && + (null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) + : legacyErrorBoundariesThatAlreadyFailed.add(this), + logCapturedError(fiber, errorInfo)); + var stack = errorInfo.stack; + this.componentDidCatch(errorInfo.value, { + componentStack: null !== stack ? stack : "" + }); + }); + return lane; +} var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { @@ -5086,14 +5238,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$65 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$65 = lastTailNode), + for (var lastTailNode$72 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$72 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$65 + null === lastTailNode$72 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$65.sibling = null); + : (lastTailNode$72.sibling = null); } } function bubbleProperties(completedWork) { @@ -5105,53 +5257,53 @@ function bubbleProperties(completedWork) { if (didBailout) if (0 !== (completedWork.mode & 2)) { for ( - var treeBaseDuration$67 = completedWork.selfBaseDuration, - child$68 = completedWork.child; - null !== child$68; + var treeBaseDuration$74 = completedWork.selfBaseDuration, + child$75 = completedWork.child; + null !== child$75; ) - (newChildLanes |= child$68.lanes | child$68.childLanes), - (subtreeFlags |= child$68.subtreeFlags & 1835008), - (subtreeFlags |= child$68.flags & 1835008), - (treeBaseDuration$67 += child$68.treeBaseDuration), - (child$68 = child$68.sibling); - completedWork.treeBaseDuration = treeBaseDuration$67; + (newChildLanes |= child$75.lanes | child$75.childLanes), + (subtreeFlags |= child$75.subtreeFlags & 1835008), + (subtreeFlags |= child$75.flags & 1835008), + (treeBaseDuration$74 += child$75.treeBaseDuration), + (child$75 = child$75.sibling); + completedWork.treeBaseDuration = treeBaseDuration$74; } else for ( - treeBaseDuration$67 = completedWork.child; - null !== treeBaseDuration$67; + treeBaseDuration$74 = completedWork.child; + null !== treeBaseDuration$74; ) (newChildLanes |= - treeBaseDuration$67.lanes | treeBaseDuration$67.childLanes), - (subtreeFlags |= treeBaseDuration$67.subtreeFlags & 1835008), - (subtreeFlags |= treeBaseDuration$67.flags & 1835008), - (treeBaseDuration$67.return = completedWork), - (treeBaseDuration$67 = treeBaseDuration$67.sibling); + treeBaseDuration$74.lanes | treeBaseDuration$74.childLanes), + (subtreeFlags |= treeBaseDuration$74.subtreeFlags & 1835008), + (subtreeFlags |= treeBaseDuration$74.flags & 1835008), + (treeBaseDuration$74.return = completedWork), + (treeBaseDuration$74 = treeBaseDuration$74.sibling); else if (0 !== (completedWork.mode & 2)) { - treeBaseDuration$67 = completedWork.actualDuration; - child$68 = completedWork.selfBaseDuration; + treeBaseDuration$74 = completedWork.actualDuration; + child$75 = completedWork.selfBaseDuration; for (var child = completedWork.child; null !== child; ) (newChildLanes |= child.lanes | child.childLanes), (subtreeFlags |= child.subtreeFlags), (subtreeFlags |= child.flags), - (treeBaseDuration$67 += child.actualDuration), - (child$68 += child.treeBaseDuration), + (treeBaseDuration$74 += child.actualDuration), + (child$75 += child.treeBaseDuration), (child = child.sibling); - completedWork.actualDuration = treeBaseDuration$67; - completedWork.treeBaseDuration = child$68; + completedWork.actualDuration = treeBaseDuration$74; + completedWork.treeBaseDuration = child$75; } else for ( - treeBaseDuration$67 = completedWork.child; - null !== treeBaseDuration$67; + treeBaseDuration$74 = completedWork.child; + null !== treeBaseDuration$74; ) (newChildLanes |= - treeBaseDuration$67.lanes | treeBaseDuration$67.childLanes), - (subtreeFlags |= treeBaseDuration$67.subtreeFlags), - (subtreeFlags |= treeBaseDuration$67.flags), - (treeBaseDuration$67.return = completedWork), - (treeBaseDuration$67 = treeBaseDuration$67.sibling); + treeBaseDuration$74.lanes | treeBaseDuration$74.childLanes), + (subtreeFlags |= treeBaseDuration$74.subtreeFlags), + (subtreeFlags |= treeBaseDuration$74.flags), + (treeBaseDuration$74.return = completedWork), + (treeBaseDuration$74 = treeBaseDuration$74.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -5528,93 +5680,34 @@ function unwindWork(workInProgress) { return null; } } -function createCapturedValue(value, source) { - try { - var info = "", - node = source; - do (info += describeFiber(node)), (node = node.return); - while (node); - var JSCompiler_inline_result = info; - } catch (x) { - JSCompiler_inline_result = - "\nError generating stack: " + x.message + "\n" + x.stack; - } - return { value: value, source: source, stack: JSCompiler_inline_result }; -} -if ( - "function" !== - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog -) - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -function logCapturedError(boundary, errorInfo) { - try { - !1 !== - ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ - componentStack: null !== errorInfo.stack ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - null !== boundary && 1 === boundary.tag ? boundary.stateNode : null - }) && console.error(errorInfo.value); - } catch (e) { - setTimeout(function() { - throw e; - }); - } -} -var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; -function createRootErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - lane.payload = { element: null }; - var error = errorInfo.value; - lane.callback = function() { - hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); - logCapturedError(fiber, errorInfo); - }; - return lane; -} -function createClassErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ("function" === typeof getDerivedStateFromError) { - var error = errorInfo.value; - lane.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error); - }; - } - var inst = fiber.stateNode; - null !== inst && - "function" === typeof inst.componentDidCatch && - (lane.callback = function() { - "function" !== typeof getDerivedStateFromError && - (null === legacyErrorBoundariesThatAlreadyFailed - ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) - : legacyErrorBoundariesThatAlreadyFailed.add(this), - logCapturedError(fiber, errorInfo)); - var stack = errorInfo.stack; - this.componentDidCatch(errorInfo.value, { - componentStack: null !== stack ? stack : "" - }); - }); - return lane; -} var PossiblyWeakSet = "function" === typeof WeakSet ? WeakSet : Set, - nextEffect = null; + nextEffect = null, + inProgressLanes = null, + inProgressRoot = null; function safelyDetachRef(current, nearestMountedAncestor) { var ref = current.ref; if (null !== ref) if ("function" === typeof ref) try { - ref(null); + if (current.mode & 2) + try { + startLayoutEffectTimer(), ref(null); + } finally { + recordLayoutEffectDuration(current); + } + else ref(null); } catch (refError) { captureCommitPhaseError(current, nearestMountedAncestor, refError); } else ref.current = null; } +function safelyCallDestroy(current, nearestMountedAncestor, destroy) { + try { + destroy(); + } catch (error) { + captureCommitPhaseError(current, nearestMountedAncestor, error); + } +} var focusedInstanceHandle = null, shouldFireAfterActiveInstanceBlur = !1; function commitBeforeMutationEffects(root, firstChild) { @@ -5709,7 +5802,7 @@ function commitBeforeMutationEffects(root, firstChild) { function commitHookEffectListUnmount( flags, finishedWork, - nearestMountedAncestor$jscomp$0 + nearestMountedAncestor ) { var updateQueue = finishedWork.updateQueue; updateQueue = null !== updateQueue ? updateQueue.lastEffect : null; @@ -5719,15 +5812,8 @@ function commitHookEffectListUnmount( if ((effect.tag & flags) === flags) { var destroy = effect.destroy; effect.destroy = void 0; - if (void 0 !== destroy) { - var current = finishedWork, - nearestMountedAncestor = nearestMountedAncestor$jscomp$0; - try { - destroy(); - } catch (error) { - captureCommitPhaseError(current, nearestMountedAncestor, error); - } - } + void 0 !== destroy && + safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy); } effect = effect.next; } while (effect !== updateQueue); @@ -5740,8 +5826,8 @@ function commitHookEffectListMount(tag, finishedWork) { var effect = (finishedWork = finishedWork.next); do { if ((effect.tag & tag) === tag) { - var create$86 = effect.create; - effect.destroy = create$86(); + var create$88 = effect.create; + effect.destroy = create$88(); } effect = effect.next; } while (effect !== finishedWork); @@ -5767,7 +5853,14 @@ function commitWork(current, finishedWork) { case 11: case 14: case 15: - commitHookEffectListUnmount(3, finishedWork, finishedWork.return); + if (finishedWork.mode & 2) + try { + startLayoutEffectTimer(), + commitHookEffectListUnmount(3, finishedWork, finishedWork.return); + } finally { + recordLayoutEffectDuration(finishedWork); + } + else commitHookEffectListUnmount(3, finishedWork, finishedWork.return); return; case 12: return; @@ -5807,18 +5900,33 @@ function attachSuspenseRetryListeners(finishedWork) { (retryCache = finishedWork.stateNode = new PossiblyWeakSet()); wakeables.forEach(function(wakeable) { var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable); - retryCache.has(wakeable) || - (retryCache.add(wakeable), wakeable.then(retry, retry)); + if (!retryCache.has(wakeable)) { + retryCache.add(wakeable); + if (isDevToolsPresent) + if (null !== inProgressLanes && null !== inProgressRoot) + restorePendingUpdaters(inProgressRoot, inProgressLanes); + else + throw Error( + "Expected finished root and lanes to be set. This is a bug in React." + ); + wakeable.then(retry, retry); + } }); } } -function commitMutationEffects(root, firstChild) { +function commitMutationEffects(root, firstChild, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; for (nextEffect = firstChild; null !== nextEffect; ) { root = nextEffect; firstChild = root.deletions; if (null !== firstChild) - for (var i = 0; i < firstChild.length; i++) { - var childToDelete = firstChild[i]; + for ( + committedLanes = 0; + committedLanes < firstChild.length; + committedLanes++ + ) { + var childToDelete = firstChild[committedLanes]; try { a: for (var node = childToDelete; ; ) { var current = node; @@ -5844,19 +5952,13 @@ function commitMutationEffects(root, firstChild) { var _effect = effect, destroy = _effect.destroy, tag = _effect.tag; - if (void 0 !== destroy && 0 !== (tag & 2)) { - _effect = current; - var nearestMountedAncestor = root; - try { - destroy(); - } catch (error) { - captureCommitPhaseError( - _effect, - nearestMountedAncestor, - error - ); - } - } + void 0 !== destroy && + 0 !== (tag & 2) && + (current.mode & 2 + ? (startLayoutEffectTimer(), + safelyCallDestroy(current, root, destroy), + recordLayoutEffectDuration(current)) + : safelyCallDestroy(current, root, destroy)); effect = effect.next; } while (effect !== firstEffect); } @@ -5867,11 +5969,20 @@ function commitMutationEffects(root, firstChild) { var instance = current.stateNode; if ("function" === typeof instance.componentWillUnmount) try { - (effect = current), + if ( + ((effect = current), (_effect = instance), (_effect.props = effect.memoizedProps), (_effect.state = effect.memoizedState), - _effect.componentWillUnmount(); + effect.mode & 2) + ) + try { + startLayoutEffectTimer(), + _effect.componentWillUnmount(); + } finally { + recordLayoutEffectDuration(effect); + } + else _effect.componentWillUnmount(); } catch (unmountError) { captureCommitPhaseError(current, root, unmountError); } @@ -5913,11 +6024,18 @@ function commitMutationEffects(root, firstChild) { if (flags & 256) { var current$jscomp$0 = root.alternate; if (null !== current$jscomp$0) { - var currentRef = current$jscomp$0.ref; - null !== currentRef && - ("function" === typeof currentRef - ? currentRef(null) - : (currentRef.current = null)); + firstChild = current$jscomp$0; + var currentRef = firstChild.ref; + if (null !== currentRef) + if ("function" === typeof currentRef) + if (firstChild.mode & 2) + try { + startLayoutEffectTimer(), currentRef(null); + } finally { + recordLayoutEffectDuration(firstChild); + } + else currentRef(null); + else currentRef.current = null; } } switch (flags & 2054) { @@ -5950,83 +6068,145 @@ function commitMutationEffects(root, firstChild) { nextEffect = root.return; } } + inProgressRoot = inProgressLanes = null; } -function commitLayoutEffects(finishedWork) { - for (nextEffect = finishedWork; null !== nextEffect; ) { - var fiber = nextEffect, - firstChild = fiber.child; - if (0 !== (fiber.subtreeFlags & 324) && null !== firstChild) - (firstChild.return = fiber), (nextEffect = firstChild); +function commitLayoutEffects(finishedWork, root, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; + for (nextEffect = finishedWork; null !== nextEffect; ) + if ( + ((root = nextEffect), + (committedLanes = root.child), + 0 !== (root.subtreeFlags & 324) && null !== committedLanes) + ) + (committedLanes.return = root), (nextEffect = committedLanes); else - for (fiber = finishedWork; null !== nextEffect; ) { - firstChild = nextEffect; - if (0 !== (firstChild.flags & 324)) { - var current = firstChild.alternate; + for (root = finishedWork; null !== nextEffect; ) { + committedLanes = nextEffect; + if (0 !== (committedLanes.flags & 324)) { + var current = committedLanes.alternate; try { - if (0 !== (firstChild.flags & 68)) - switch (firstChild.tag) { + if (0 !== (committedLanes.flags & 68)) + switch (committedLanes.tag) { case 0: case 11: case 15: - commitHookEffectListMount(3, firstChild); + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + commitHookEffectListMount(3, committedLanes); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else commitHookEffectListMount(3, committedLanes); break; case 1: - var instance = firstChild.stateNode; - if (firstChild.flags & 4) - if (null === current) instance.componentDidMount(); + var instance = committedLanes.stateNode; + if (committedLanes.flags & 4) + if (null === current) + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + instance.componentDidMount(); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else instance.componentDidMount(); else { var prevProps = - firstChild.elementType === firstChild.type - ? current.memoizedProps - : resolveDefaultProps( - firstChild.type, - current.memoizedProps + committedLanes.elementType === committedLanes.type + ? current.memoizedProps + : resolveDefaultProps( + committedLanes.type, + current.memoizedProps + ), + prevState = current.memoizedState; + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate ); - instance.componentDidUpdate( - prevProps, - current.memoizedState, - instance.__reactInternalSnapshotBeforeUpdate - ); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate + ); } - var updateQueue = firstChild.updateQueue; + var updateQueue = committedLanes.updateQueue; null !== updateQueue && - commitUpdateQueue(firstChild, updateQueue, instance); + commitUpdateQueue(committedLanes, updateQueue, instance); break; case 3: - var updateQueue$87 = firstChild.updateQueue; - if (null !== updateQueue$87) { - var instance$88 = null; - if (null !== firstChild.child) - switch (firstChild.child.tag) { + var updateQueue$90 = committedLanes.updateQueue; + if (null !== updateQueue$90) { + var instance$91 = null; + if (null !== committedLanes.child) + switch (committedLanes.child.tag) { case 5: - instance$88 = firstChild.child.stateNode.canonical; + instance$91 = + committedLanes.child.stateNode.canonical; break; case 1: - instance$88 = firstChild.child.stateNode; + instance$91 = committedLanes.child.stateNode; } - commitUpdateQueue(firstChild, updateQueue$87, instance$88); + commitUpdateQueue( + committedLanes, + updateQueue$90, + instance$91 + ); } break; case 5: - null === current && firstChild.flags & 4 && shim(); + null === current && committedLanes.flags & 4 && shim(); break; case 6: break; case 4: break; case 12: - var onRender = firstChild.memoizedProps.onRender; - instance$88 = commitTime; + var _finishedWork$memoize2 = committedLanes.memoizedProps, + onCommit = _finishedWork$memoize2.onCommit, + onRender = _finishedWork$memoize2.onRender, + effectDuration = committedLanes.stateNode.effectDuration; + instance$91 = commitTime; current = null === current ? "mount" : "update"; + currentUpdateIsNested && (current = "nested-update"); "function" === typeof onRender && onRender( - firstChild.memoizedProps.id, + committedLanes.memoizedProps.id, + current, + committedLanes.actualDuration, + committedLanes.treeBaseDuration, + committedLanes.actualStartTime, + instance$91 + ); + "function" === typeof onCommit && + onCommit( + committedLanes.memoizedProps.id, current, - firstChild.actualDuration, - firstChild.treeBaseDuration, - firstChild.actualStartTime, - instance$88 + effectDuration, + instance$91 ); + enqueuePendingPassiveProfilerEffect(committedLanes); + var parentFiber = committedLanes.return; + a: for (; null !== parentFiber; ) { + switch (parentFiber.tag) { + case 3: + parentFiber.stateNode.effectDuration += effectDuration; + break a; + case 12: + parentFiber.stateNode.effectDuration += effectDuration; + break a; + } + parentFiber = parentFiber.return; + } break; case 13: break; @@ -6041,40 +6221,51 @@ function commitLayoutEffects(finishedWork) { "This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue." ); } - if (firstChild.flags & 256) { - instance$88 = void 0; - var ref = firstChild.ref; + if (committedLanes.flags & 256) { + instance$91 = void 0; + current = committedLanes; + var ref = current.ref; if (null !== ref) { - var instance$jscomp$0 = firstChild.stateNode; - switch (firstChild.tag) { + var instance$jscomp$0 = current.stateNode; + switch (current.tag) { case 5: - instance$88 = instance$jscomp$0.canonical; + instance$91 = instance$jscomp$0.canonical; break; default: - instance$88 = instance$jscomp$0; + instance$91 = instance$jscomp$0; } - "function" === typeof ref - ? ref(instance$88) - : (ref.current = instance$88); + if ("function" === typeof ref) + if (current.mode & 2) + try { + startLayoutEffectTimer(), ref(instance$91); + } finally { + recordLayoutEffectDuration(current); + } + else ref(instance$91); + else ref.current = instance$91; } } } catch (error) { - captureCommitPhaseError(firstChild, firstChild.return, error); + captureCommitPhaseError( + committedLanes, + committedLanes.return, + error + ); } } - if (firstChild === fiber) { + if (committedLanes === root) { nextEffect = null; break; } - instance$88 = firstChild.sibling; - if (null !== instance$88) { - instance$88.return = firstChild.return; - nextEffect = instance$88; + instance$91 = committedLanes.sibling; + if (null !== instance$91) { + instance$91.return = committedLanes.return; + nextEffect = instance$91; break; } - nextEffect = firstChild.return; + nextEffect = committedLanes.return; } - } + inProgressRoot = inProgressLanes = null; } var ceil = Math.ceil, ReactCurrentDispatcher$2 = ReactSharedInternals.ReactCurrentDispatcher, @@ -6099,6 +6290,7 @@ var ceil = Math.ceil, rootDoesHavePassiveEffects = !1, rootWithPendingPassiveEffects = null, pendingPassiveEffectsLanes = 0, + pendingPassiveProfilerEffects = [], nestedUpdateCount = 0, rootWithNestedUpdates = null, currentEventTime = -1, @@ -6112,6 +6304,8 @@ function requestEventTime() { } function requestUpdateLane(fiber) { if (0 === (fiber.mode & 1)) return 1; + if (0 !== (executionContext & 8) && 0 !== workInProgressRootRenderLanes) + return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; if (0 !== ReactCurrentBatchConfig.transition) return ( 0 === currentEventTransitionLane && @@ -6133,9 +6327,10 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { )); var root = markUpdateLaneFromFiberToRoot(fiber, lane); if (null === root) return null; + isDevToolsPresent && addFiberToLanesMap(root, fiber, lane); markRootUpdated(root, lane, eventTime); root === workInProgressRoot && - ((workInProgressRootUpdatedLanes |= lane), + (0 === (executionContext & 8) && (workInProgressRootUpdatedLanes |= lane), 4 === workInProgressRootExitStatus && markRootSuspended$1(root, workInProgressRootRenderLanes)); 1 === lane @@ -6234,6 +6429,7 @@ function ensureRootIsScheduled(root, currentTime) { } } function performConcurrentWorkOnRoot(root, didTimeout) { + nestedUpdateScheduled = currentUpdateIsNested = !1; currentEventTime = -1; currentEventTransitionLane = 0; if (0 !== (executionContext & 24)) @@ -6260,9 +6456,17 @@ function performConcurrentWorkOnRoot(root, didTimeout) { if ( workInProgressRoot !== root || workInProgressRootRenderLanes !== didTimeout - ) - (workInProgressRootRenderTargetTime = now() + 500), - prepareFreshStack(root, didTimeout); + ) { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + 0 < memoizedUpdaters.size && + (restorePendingUpdaters(root, workInProgressRootRenderLanes), + memoizedUpdaters.clear()); + movePendingFibersToMemoized(root, didTimeout); + } + workInProgressRootRenderTargetTime = now() + 500; + prepareFreshStack(root, didTimeout); + } do try { workLoopConcurrent(); @@ -6329,14 +6533,13 @@ function performConcurrentWorkOnRoot(root, didTimeout) { markRootSuspended$1(root, lanes); if ((lanes & 4194240) === lanes) break; didTimeout = root.eventTimes; - for (JSCompiler_inline_result = -1; 0 < lanes; ) { - var index$4 = 31 - clz32(lanes); - prevDispatcher = 1 << index$4; - index$4 = didTimeout[index$4]; - index$4 > JSCompiler_inline_result && - (JSCompiler_inline_result = index$4); - lanes &= ~prevDispatcher; - } + for (JSCompiler_inline_result = -1; 0 < lanes; ) + (memoizedUpdaters = 31 - clz32(lanes)), + (prevDispatcher = 1 << memoizedUpdaters), + (memoizedUpdaters = didTimeout[memoizedUpdaters]), + memoizedUpdaters > JSCompiler_inline_result && + (JSCompiler_inline_result = memoizedUpdaters), + (lanes &= ~prevDispatcher); lanes = JSCompiler_inline_result; lanes = now() - lanes; lanes = @@ -6387,6 +6590,8 @@ function markRootSuspended$1(root, suspendedLanes) { } } function performSyncWorkOnRoot(root) { + currentUpdateIsNested = nestedUpdateScheduled; + nestedUpdateScheduled = !1; if (0 !== (executionContext & 24)) throw Error("Should not already be working."); flushPassiveEffects(); @@ -6522,6 +6727,7 @@ function handleError(root$jscomp$0, thrownValue) { value = thrownValue; thrownValue = workInProgressRootRenderLanes; sourceFiber.flags |= 8192; + isDevToolsPresent && restorePendingUpdaters(root, thrownValue); if ( null !== value && "object" === typeof value && @@ -6543,15 +6749,15 @@ function handleError(root$jscomp$0, thrownValue) { } var hasInvisibleParentBoundary = 0 !== (suspenseStackCursor.current & 1), - workInProgress$81 = returnFiber; + workInProgress$32 = returnFiber; do { var JSCompiler_temp; - if ((JSCompiler_temp = 13 === workInProgress$81.tag)) { - var nextState = workInProgress$81.memoizedState; + if ((JSCompiler_temp = 13 === workInProgress$32.tag)) { + var nextState = workInProgress$32.memoizedState; if (null !== nextState) JSCompiler_temp = null !== nextState.dehydrated ? !0 : !1; else { - var props = workInProgress$81.memoizedProps; + var props = workInProgress$32.memoizedProps; JSCompiler_temp = void 0 === props.fallback ? !1 @@ -6563,17 +6769,17 @@ function handleError(root$jscomp$0, thrownValue) { } } if (JSCompiler_temp) { - var wakeables = workInProgress$81.updateQueue; + var wakeables = workInProgress$32.updateQueue; if (null === wakeables) { var updateQueue = new Set(); updateQueue.add(wakeable); - workInProgress$81.updateQueue = updateQueue; + workInProgress$32.updateQueue = updateQueue; } else wakeables.add(wakeable); if ( - 0 === (workInProgress$81.mode & 1) && - workInProgress$81 !== returnFiber + 0 === (workInProgress$32.mode & 1) && + workInProgress$32 !== returnFiber ) { - workInProgress$81.flags |= 128; + workInProgress$32.flags |= 128; sourceFiber.flags |= 32768; sourceFiber.flags &= -10053; if (1 === sourceFiber.tag) @@ -6604,14 +6810,15 @@ function handleError(root$jscomp$0, thrownValue) { wakeable, sourceFiber ); + isDevToolsPresent && restorePendingUpdaters(root, sourceFiber); wakeable.then(ping, ping); } - workInProgress$81.flags |= 16384; - workInProgress$81.lanes = thrownValue; + workInProgress$32.flags |= 16384; + workInProgress$32.lanes = thrownValue; break a; } - workInProgress$81 = workInProgress$81.return; - } while (null !== workInProgress$81); + workInProgress$32 = workInProgress$32.return; + } while (null !== workInProgress$32); value = Error( (getComponentNameFromFiber(sourceFiber) || "A React component") + " suspended while rendering, but no fallback UI was specified.\n\nAdd a component higher in the tree to provide a loading indicator or placeholder to display." @@ -6620,47 +6827,47 @@ function handleError(root$jscomp$0, thrownValue) { 5 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2); value = createCapturedValue(value, sourceFiber); - workInProgress$81 = returnFiber; + workInProgress$32 = returnFiber; do { - switch (workInProgress$81.tag) { + switch (workInProgress$32.tag) { case 3: root = value; - workInProgress$81.flags |= 16384; + workInProgress$32.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$81.lanes |= thrownValue; - var update$82 = createRootErrorUpdate( - workInProgress$81, + workInProgress$32.lanes |= thrownValue; + var update$33 = createRootErrorUpdate( + workInProgress$32, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$81, update$82); + enqueueCapturedUpdate(workInProgress$32, update$33); break a; case 1: root = value; - var ctor = workInProgress$81.type, - instance = workInProgress$81.stateNode; + var ctor = workInProgress$32.type, + instance = workInProgress$32.stateNode; if ( - 0 === (workInProgress$81.flags & 128) && + 0 === (workInProgress$32.flags & 128) && ("function" === typeof ctor.getDerivedStateFromError || (null !== instance && "function" === typeof instance.componentDidCatch && (null === legacyErrorBoundariesThatAlreadyFailed || !legacyErrorBoundariesThatAlreadyFailed.has(instance)))) ) { - workInProgress$81.flags |= 16384; + workInProgress$32.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$81.lanes |= thrownValue; - var update$85 = createClassErrorUpdate( - workInProgress$81, + workInProgress$32.lanes |= thrownValue; + var update$36 = createClassErrorUpdate( + workInProgress$32, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$81, update$85); + enqueueCapturedUpdate(workInProgress$32, update$36); break a; } } - workInProgress$81 = workInProgress$81.return; - } while (null !== workInProgress$81); + workInProgress$32 = workInProgress$32.return; + } while (null !== workInProgress$32); } completeUnitOfWork(erroredWork); } catch (yetAnotherThrownValue) { @@ -6682,8 +6889,16 @@ function renderRootSync(root, lanes) { var prevExecutionContext = executionContext; executionContext |= 8; var prevDispatcher = pushDispatcher(); - (workInProgressRoot === root && workInProgressRootRenderLanes === lanes) || + if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + 0 < memoizedUpdaters.size && + (restorePendingUpdaters(root, workInProgressRootRenderLanes), + memoizedUpdaters.clear()); + movePendingFibersToMemoized(root, lanes); + } prepareFreshStack(root, lanes); + } do try { workLoopSync(); @@ -6824,7 +7039,7 @@ function commitRootImpl(root, renderPriorityLevel) { ReactCurrentOwner$2.current = null; commitBeforeMutationEffects(root, finishedWork); commitTime = now$1(); - commitMutationEffects(root, finishedWork); + commitMutationEffects(root, finishedWork, lanes); root.current = finishedWork; commitLayoutEffects(finishedWork, root, lanes); requestPaint(); @@ -6839,11 +7054,13 @@ function commitRootImpl(root, renderPriorityLevel) { remainingLanes = root.pendingLanes; 0 === remainingLanes && (legacyErrorBoundariesThatAlreadyFailed = null); 0 !== (remainingLanes & 1) - ? root === rootWithNestedUpdates - ? nestedUpdateCount++ - : ((nestedUpdateCount = 0), (rootWithNestedUpdates = root)) + ? ((nestedUpdateScheduled = !0), + root === rootWithNestedUpdates + ? nestedUpdateCount++ + : ((nestedUpdateCount = 0), (rootWithNestedUpdates = root))) : (nestedUpdateCount = 0); onCommitRoot(finishedWork.stateNode, renderPriorityLevel); + isDevToolsPresent && root.memoizedUpdaters.clear(); ensureRootIsScheduled(root, now()); if (hasUncaughtError) throw ((hasUncaughtError = !1), @@ -6884,12 +7101,17 @@ function flushPassiveEffects() { for (var i = 0; i < deletions.length; i++) { var fiberToDelete = deletions[i]; for (nextEffect = fiberToDelete; null !== nextEffect; ) { - var fiber$jscomp$0 = nextEffect; - switch (fiber$jscomp$0.tag) { + var fiber$jscomp$0 = nextEffect, + current = fiber$jscomp$0; + switch (current.tag) { case 0: case 11: case 15: - commitHookEffectListUnmount(4, fiber$jscomp$0, fiber); + current.mode & 2 + ? ((passiveEffectStartTime = now$1()), + commitHookEffectListUnmount(4, current, fiber), + recordPassiveEffectDuration(current)) + : commitHookEffectListUnmount(4, current, fiber); } var child$jscomp$0 = fiber$jscomp$0.child; if (null !== child$jscomp$0) @@ -6935,11 +7157,15 @@ function flushPassiveEffects() { b: for (; null !== nextEffect; ) { fiber = nextEffect; if (0 !== (fiber.flags & 1024)) - switch (fiber.tag) { + switch (((i = fiber), i.tag)) { case 0: case 11: case 15: - commitHookEffectListUnmount(5, fiber, fiber.return); + i.mode & 2 + ? ((passiveEffectStartTime = now$1()), + commitHookEffectListUnmount(5, i, i.return), + recordPassiveEffectDuration(i)) + : commitHookEffectListUnmount(5, i, i.return); } var sibling$jscomp$0 = fiber.sibling; if (null !== sibling$jscomp$0) { @@ -6961,11 +7187,18 @@ function flushPassiveEffects() { deletions = nextEffect; if (0 !== (deletions.flags & 1024)) try { - switch (deletions.tag) { + switch (((fiberToDelete = deletions), fiberToDelete.tag)) { case 0: case 11: case 15: - commitHookEffectListMount(5, deletions); + if (fiberToDelete.mode & 2) { + passiveEffectStartTime = now$1(); + try { + commitHookEffectListMount(5, fiberToDelete); + } finally { + recordPassiveEffectDuration(fiberToDelete); + } + } else commitHookEffectListMount(5, fiberToDelete); } } catch (error) { captureCommitPhaseError(deletions, deletions.return, error); @@ -6983,6 +7216,43 @@ function flushPassiveEffects() { nextEffect = deletions.return; } } + finishedWork = pendingPassiveProfilerEffects; + pendingPassiveProfilerEffects = []; + for (firstChild = 0; firstChild < finishedWork.length; firstChild++) { + var finishedWork$jscomp$0 = finishedWork[firstChild]; + if (0 !== (finishedWork$jscomp$0.flags & 4)) + switch (finishedWork$jscomp$0.tag) { + case 12: + var passiveEffectDuration = + finishedWork$jscomp$0.stateNode.passiveEffectDuration, + _finishedWork$memoize = finishedWork$jscomp$0.memoizedProps, + id = _finishedWork$memoize.id, + onPostCommit = _finishedWork$memoize.onPostCommit; + sibling$jscomp$1 = commitTime; + var phase = + null === finishedWork$jscomp$0.alternate ? "mount" : "update"; + currentUpdateIsNested && (phase = "nested-update"); + "function" === typeof onPostCommit && + onPostCommit( + id, + phase, + passiveEffectDuration, + sibling$jscomp$1 + ); + var parentFiber = finishedWork$jscomp$0.return; + b: for (; null !== parentFiber; ) { + switch (parentFiber.tag) { + case 3: + parentFiber.stateNode.passiveEffectDuration += passiveEffectDuration; + break b; + case 12: + parentFiber.stateNode.passiveEffectDuration += passiveEffectDuration; + break b; + } + parentFiber = parentFiber.return; + } + } + } executionContext = prevExecutionContext; flushSyncCallbacks(); if ( @@ -6992,6 +7262,9 @@ function flushPassiveEffects() { try { injectedHook.onPostCommitFiberRoot(rendererID, renderPriority); } catch (err) {} + var stateNode = renderPriority.current.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; JSCompiler_inline_result = !0; } return JSCompiler_inline_result; @@ -7002,6 +7275,15 @@ function flushPassiveEffects() { } return !1; } +function enqueuePendingPassiveProfilerEffect(fiber) { + pendingPassiveProfilerEffects.push(fiber); + rootDoesHavePassiveEffects || + ((rootDoesHavePassiveEffects = !0), + scheduleCallback(NormalPriority, function() { + flushPassiveEffects(); + return null; + })); +} function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) { sourceFiber = createCapturedValue(error, sourceFiber); sourceFiber = createRootErrorUpdate(rootFiber, sourceFiber, 1); @@ -7128,6 +7410,9 @@ beginWork$1 = function(current, workInProgress, renderLanes) { case 12: 0 !== (renderLanes & workInProgress.childLanes) && (workInProgress.flags |= 4); + updateLanes = workInProgress.stateNode; + updateLanes.effectDuration = 0; + updateLanes.passiveEffectDuration = 0; break; case 13: if (null !== workInProgress.memoizedState) { @@ -7422,6 +7707,9 @@ beginWork$1 = function(current, workInProgress, renderLanes) { case 12: return ( (workInProgress.flags |= 4), + (updateLanes = workInProgress.stateNode), + (updateLanes.effectDuration = 0), + (updateLanes.passiveEffectDuration = 0), reconcileChildren( current, workInProgress, @@ -7601,6 +7889,12 @@ beginWork$1 = function(current, workInProgress, renderLanes) { "). This error is likely caused by a bug in React. Please file an issue." ); }; +function restorePendingUpdaters(root, lanes) { + isDevToolsPresent && + root.memoizedUpdaters.forEach(function(schedulingFiber) { + addFiberToLanesMap(root, schedulingFiber, lanes); + }); +} function FiberNode(tag, pendingProps, key, mode) { this.tag = tag; this.key = key; @@ -7697,7 +7991,7 @@ function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = 8; - mode |= 24; + mode |= 8; break; case REACT_PROFILER_TYPE: return ( @@ -7806,6 +8100,10 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.expirationTimes = createLaneMap(-1); this.entangledLanes = this.finishedLanes = this.mutableReadLanes = this.expiredLanes = this.pingedLanes = this.suspendedLanes = this.pendingLanes = 0; this.entanglements = createLaneMap(0); + this.passiveEffectDuration = this.effectDuration = 0; + this.memoizedUpdaters = new Set(); + containerInfo = this.pendingUpdatersLaneMap = []; + for (tag = 0; 31 > tag; tag++) containerInfo.push(new Set()); } function createPortal(children, containerInfo, implementation) { var key = @@ -7920,10 +8218,10 @@ batchedUpdatesImpl = function(fn, a) { } }; var roots = new Map(), - devToolsConfig$jscomp$inline_966 = { + devToolsConfig$jscomp$inline_972 = { findFiberByHostInstance: getInstanceFromInstance, bundleType: 0, - version: "17.0.3-2d8d133e1", + version: "17.0.3-0eea57724", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function() { @@ -7938,17 +8236,18 @@ var roots = new Map(), }.bind(null, findNodeHandle) } }; -var internals$jscomp$inline_1208 = { - bundleType: devToolsConfig$jscomp$inline_966.bundleType, - version: devToolsConfig$jscomp$inline_966.version, - rendererPackageName: devToolsConfig$jscomp$inline_966.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_966.rendererConfig, +var internals$jscomp$inline_1230 = { + bundleType: devToolsConfig$jscomp$inline_972.bundleType, + version: devToolsConfig$jscomp$inline_972.version, + rendererPackageName: devToolsConfig$jscomp$inline_972.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_972.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, overrideProps: null, overridePropsDeletePath: null, overridePropsRenamePath: null, + setErrorHandler: null, setSuspenseHandler: null, scheduleUpdate: null, currentDispatcherRef: ReactSharedInternals.ReactCurrentDispatcher, @@ -7957,26 +8256,26 @@ var internals$jscomp$inline_1208 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_966.findFiberByHostInstance || + devToolsConfig$jscomp$inline_972.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "17.0.3-2d8d133e1" + reconcilerVersion: "17.0.3-0eea57724" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_1209 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_1231 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_1209.isDisabled && - hook$jscomp$inline_1209.supportsFiber + !hook$jscomp$inline_1231.isDisabled && + hook$jscomp$inline_1231.supportsFiber ) try { - (rendererID = hook$jscomp$inline_1209.inject( - internals$jscomp$inline_1208 + (rendererID = hook$jscomp$inline_1231.inject( + internals$jscomp$inline_1230 )), - (injectedHook = hook$jscomp$inline_1209); + (injectedHook = hook$jscomp$inline_1231); } catch (err) {} } exports.createPortal = function(children, containerTag) { diff --git a/Libraries/Renderer/implementations/ReactFabric-profiling.js b/Libraries/Renderer/implementations/ReactFabric-profiling.js index ce7c222db6c7b4..73e756699601fe 100644 --- a/Libraries/Renderer/implementations/ReactFabric-profiling.js +++ b/Libraries/Renderer/implementations/ReactFabric-profiling.js @@ -8,7 +8,7 @@ * @nolint * @providesModule ReactFabric-profiling * @preventMunge - * @generated SignedSource<<06caca3d95f98101d397316857fe09c9>> + * @generated SignedSource<> */ "use strict"; @@ -931,7 +931,7 @@ eventPluginOrder = Array.prototype.slice.call([ "ReactNativeBridgeEventPlugin" ]); recomputePluginOrdering(); -var injectedNamesToPlugins$jscomp$inline_221 = { +var injectedNamesToPlugins$jscomp$inline_224 = { ResponderEventPlugin: ResponderEventPlugin, ReactNativeBridgeEventPlugin: { eventTypes: {}, @@ -966,34 +966,34 @@ var injectedNamesToPlugins$jscomp$inline_221 = { } } }, - isOrderingDirty$jscomp$inline_222 = !1, - pluginName$jscomp$inline_223; -for (pluginName$jscomp$inline_223 in injectedNamesToPlugins$jscomp$inline_221) + isOrderingDirty$jscomp$inline_225 = !1, + pluginName$jscomp$inline_226; +for (pluginName$jscomp$inline_226 in injectedNamesToPlugins$jscomp$inline_224) if ( - injectedNamesToPlugins$jscomp$inline_221.hasOwnProperty( - pluginName$jscomp$inline_223 + injectedNamesToPlugins$jscomp$inline_224.hasOwnProperty( + pluginName$jscomp$inline_226 ) ) { - var pluginModule$jscomp$inline_224 = - injectedNamesToPlugins$jscomp$inline_221[pluginName$jscomp$inline_223]; + var pluginModule$jscomp$inline_227 = + injectedNamesToPlugins$jscomp$inline_224[pluginName$jscomp$inline_226]; if ( - !namesToPlugins.hasOwnProperty(pluginName$jscomp$inline_223) || - namesToPlugins[pluginName$jscomp$inline_223] !== - pluginModule$jscomp$inline_224 + !namesToPlugins.hasOwnProperty(pluginName$jscomp$inline_226) || + namesToPlugins[pluginName$jscomp$inline_226] !== + pluginModule$jscomp$inline_227 ) { - if (namesToPlugins[pluginName$jscomp$inline_223]) + if (namesToPlugins[pluginName$jscomp$inline_226]) throw Error( "EventPluginRegistry: Cannot inject two different event plugins using the same name, `" + - pluginName$jscomp$inline_223 + + pluginName$jscomp$inline_226 + "`." ); namesToPlugins[ - pluginName$jscomp$inline_223 - ] = pluginModule$jscomp$inline_224; - isOrderingDirty$jscomp$inline_222 = !0; + pluginName$jscomp$inline_226 + ] = pluginModule$jscomp$inline_227; + isOrderingDirty$jscomp$inline_225 = !0; } } -isOrderingDirty$jscomp$inline_222 && recomputePluginOrdering(); +isOrderingDirty$jscomp$inline_225 && recomputePluginOrdering(); function getInstanceFromInstance(instanceHandle) { return instanceHandle; } @@ -1830,6 +1830,36 @@ function markRootEntangled(root, entangledLanes) { rootEntangledLanes &= ~lane; } } +function addFiberToLanesMap(root, fiber, lanes) { + if (isDevToolsPresent) + for (root = root.pendingUpdatersLaneMap; 0 < lanes; ) { + var index$9 = 31 - clz32(lanes), + lane = 1 << index$9; + root[index$9].add(fiber); + lanes &= ~lane; + } +} +function movePendingFibersToMemoized(root, lanes) { + if (isDevToolsPresent) + for ( + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap, + memoizedUpdaters = root.memoizedUpdaters; + 0 < lanes; + + ) { + var index$10 = 31 - clz32(lanes); + root = 1 << index$10; + index$10 = pendingUpdatersLaneMap[index$10]; + 0 < index$10.size && + (index$10.forEach(function(fiber) { + var alternate = fiber.alternate; + (null !== alternate && memoizedUpdaters.has(alternate)) || + memoizedUpdaters.add(fiber); + }), + index$10.clear()); + lanes &= ~root; + } +} var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log = Math.log, LN2 = Math.LN2; @@ -2199,8 +2229,7 @@ function readContext(context) { lastContextDependency = context; currentlyRenderingFiber.dependencies = { lanes: 0, - firstContext: context, - responders: null + firstContext: context }; } else lastContextDependency = lastContextDependency.next = context; return value; @@ -2241,7 +2270,9 @@ function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; null !== updateQueue && ((updateQueue = updateQueue.shared), - null !== workInProgressRoot && 0 !== (fiber.mode & 1) + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) ? ((fiber = updateQueue.interleaved), null === fiber ? ((update.next = update), @@ -3799,7 +3830,11 @@ function dispatchAction(fiber, queue, action) { : ((update.next = lane.next), (lane.next = update)), (queue.pending = update); else { - if (null !== workInProgressRoot && 0 !== (fiber.mode & 1)) { + if ( + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) + ) { var interleaved = queue.interleaved; null === interleaved ? ((update.next = update), @@ -4039,7 +4074,11 @@ var ContextOnlyDispatcher = { }, now$1 = Scheduler.unstable_now, commitTime = 0, - profilerStartTime = -1; + layoutEffectStartTime = -1, + profilerStartTime = -1, + passiveEffectStartTime = -1, + currentUpdateIsNested = !1, + nestedUpdateScheduled = !1; function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { if (0 <= profilerStartTime) { var elapsedTime = now$1() - profilerStartTime; @@ -4048,10 +4087,123 @@ function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { profilerStartTime = -1; } } +function recordLayoutEffectDuration(fiber) { + if (0 <= layoutEffectStartTime) { + var elapsedTime = now$1() - layoutEffectStartTime; + layoutEffectStartTime = -1; + for (fiber = fiber.return; null !== fiber; ) { + switch (fiber.tag) { + case 3: + fiber.stateNode.effectDuration += elapsedTime; + return; + case 12: + fiber.stateNode.effectDuration += elapsedTime; + return; + } + fiber = fiber.return; + } + } +} +function recordPassiveEffectDuration(fiber) { + if (0 <= passiveEffectStartTime) { + var elapsedTime = now$1() - passiveEffectStartTime; + passiveEffectStartTime = -1; + for (fiber = fiber.return; null !== fiber; ) { + switch (fiber.tag) { + case 3: + fiber = fiber.stateNode; + null !== fiber && (fiber.passiveEffectDuration += elapsedTime); + return; + case 12: + fiber = fiber.stateNode; + null !== fiber && (fiber.passiveEffectDuration += elapsedTime); + return; + } + fiber = fiber.return; + } + } +} +function startLayoutEffectTimer() { + layoutEffectStartTime = now$1(); +} function transferActualDuration(fiber) { for (var child = fiber.child; child; ) (fiber.actualDuration += child.actualDuration), (child = child.sibling); } +function createCapturedValue(value, source) { + try { + var info = "", + node = source; + do (info += describeFiber(node)), (node = node.return); + while (node); + var JSCompiler_inline_result = info; + } catch (x) { + JSCompiler_inline_result = + "\nError generating stack: " + x.message + "\n" + x.stack; + } + return { value: value, source: source, stack: JSCompiler_inline_result }; +} +if ( + "function" !== + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog +) + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +function logCapturedError(boundary, errorInfo) { + try { + !1 !== + ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ + componentStack: null !== errorInfo.stack ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + null !== boundary && 1 === boundary.tag ? boundary.stateNode : null + }) && console.error(errorInfo.value); + } catch (e) { + setTimeout(function() { + throw e; + }); + } +} +var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; +function createRootErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + lane.payload = { element: null }; + var error = errorInfo.value; + lane.callback = function() { + hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); + logCapturedError(fiber, errorInfo); + }; + return lane; +} +function createClassErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if ("function" === typeof getDerivedStateFromError) { + var error = errorInfo.value; + lane.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error); + }; + } + var inst = fiber.stateNode; + null !== inst && + "function" === typeof inst.componentDidCatch && + (lane.callback = function() { + "function" !== typeof getDerivedStateFromError && + (null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) + : legacyErrorBoundariesThatAlreadyFailed.add(this), + logCapturedError(fiber, errorInfo)); + var stack = errorInfo.stack; + this.componentDidCatch(errorInfo.value, { + componentStack: null !== stack ? stack : "" + }); + }); + return lane; +} var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { @@ -5085,14 +5237,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$65 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$65 = lastTailNode), + for (var lastTailNode$72 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$72 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$65 + null === lastTailNode$72 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$65.sibling = null); + : (lastTailNode$72.sibling = null); } } function bubbleProperties(completedWork) { @@ -5104,53 +5256,53 @@ function bubbleProperties(completedWork) { if (didBailout) if (0 !== (completedWork.mode & 2)) { for ( - var treeBaseDuration$67 = completedWork.selfBaseDuration, - child$68 = completedWork.child; - null !== child$68; + var treeBaseDuration$74 = completedWork.selfBaseDuration, + child$75 = completedWork.child; + null !== child$75; ) - (newChildLanes |= child$68.lanes | child$68.childLanes), - (subtreeFlags |= child$68.subtreeFlags & 1835008), - (subtreeFlags |= child$68.flags & 1835008), - (treeBaseDuration$67 += child$68.treeBaseDuration), - (child$68 = child$68.sibling); - completedWork.treeBaseDuration = treeBaseDuration$67; + (newChildLanes |= child$75.lanes | child$75.childLanes), + (subtreeFlags |= child$75.subtreeFlags & 1835008), + (subtreeFlags |= child$75.flags & 1835008), + (treeBaseDuration$74 += child$75.treeBaseDuration), + (child$75 = child$75.sibling); + completedWork.treeBaseDuration = treeBaseDuration$74; } else for ( - treeBaseDuration$67 = completedWork.child; - null !== treeBaseDuration$67; + treeBaseDuration$74 = completedWork.child; + null !== treeBaseDuration$74; ) (newChildLanes |= - treeBaseDuration$67.lanes | treeBaseDuration$67.childLanes), - (subtreeFlags |= treeBaseDuration$67.subtreeFlags & 1835008), - (subtreeFlags |= treeBaseDuration$67.flags & 1835008), - (treeBaseDuration$67.return = completedWork), - (treeBaseDuration$67 = treeBaseDuration$67.sibling); + treeBaseDuration$74.lanes | treeBaseDuration$74.childLanes), + (subtreeFlags |= treeBaseDuration$74.subtreeFlags & 1835008), + (subtreeFlags |= treeBaseDuration$74.flags & 1835008), + (treeBaseDuration$74.return = completedWork), + (treeBaseDuration$74 = treeBaseDuration$74.sibling); else if (0 !== (completedWork.mode & 2)) { - treeBaseDuration$67 = completedWork.actualDuration; - child$68 = completedWork.selfBaseDuration; + treeBaseDuration$74 = completedWork.actualDuration; + child$75 = completedWork.selfBaseDuration; for (var child = completedWork.child; null !== child; ) (newChildLanes |= child.lanes | child.childLanes), (subtreeFlags |= child.subtreeFlags), (subtreeFlags |= child.flags), - (treeBaseDuration$67 += child.actualDuration), - (child$68 += child.treeBaseDuration), + (treeBaseDuration$74 += child.actualDuration), + (child$75 += child.treeBaseDuration), (child = child.sibling); - completedWork.actualDuration = treeBaseDuration$67; - completedWork.treeBaseDuration = child$68; + completedWork.actualDuration = treeBaseDuration$74; + completedWork.treeBaseDuration = child$75; } else for ( - treeBaseDuration$67 = completedWork.child; - null !== treeBaseDuration$67; + treeBaseDuration$74 = completedWork.child; + null !== treeBaseDuration$74; ) (newChildLanes |= - treeBaseDuration$67.lanes | treeBaseDuration$67.childLanes), - (subtreeFlags |= treeBaseDuration$67.subtreeFlags), - (subtreeFlags |= treeBaseDuration$67.flags), - (treeBaseDuration$67.return = completedWork), - (treeBaseDuration$67 = treeBaseDuration$67.sibling); + treeBaseDuration$74.lanes | treeBaseDuration$74.childLanes), + (subtreeFlags |= treeBaseDuration$74.subtreeFlags), + (subtreeFlags |= treeBaseDuration$74.flags), + (treeBaseDuration$74.return = completedWork), + (treeBaseDuration$74 = treeBaseDuration$74.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -5527,93 +5679,34 @@ function unwindWork(workInProgress) { return null; } } -function createCapturedValue(value, source) { - try { - var info = "", - node = source; - do (info += describeFiber(node)), (node = node.return); - while (node); - var JSCompiler_inline_result = info; - } catch (x) { - JSCompiler_inline_result = - "\nError generating stack: " + x.message + "\n" + x.stack; - } - return { value: value, source: source, stack: JSCompiler_inline_result }; -} -if ( - "function" !== - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog -) - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -function logCapturedError(boundary, errorInfo) { - try { - !1 !== - ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ - componentStack: null !== errorInfo.stack ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - null !== boundary && 1 === boundary.tag ? boundary.stateNode : null - }) && console.error(errorInfo.value); - } catch (e) { - setTimeout(function() { - throw e; - }); - } -} -var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; -function createRootErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - lane.payload = { element: null }; - var error = errorInfo.value; - lane.callback = function() { - hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); - logCapturedError(fiber, errorInfo); - }; - return lane; -} -function createClassErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ("function" === typeof getDerivedStateFromError) { - var error = errorInfo.value; - lane.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error); - }; - } - var inst = fiber.stateNode; - null !== inst && - "function" === typeof inst.componentDidCatch && - (lane.callback = function() { - "function" !== typeof getDerivedStateFromError && - (null === legacyErrorBoundariesThatAlreadyFailed - ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) - : legacyErrorBoundariesThatAlreadyFailed.add(this), - logCapturedError(fiber, errorInfo)); - var stack = errorInfo.stack; - this.componentDidCatch(errorInfo.value, { - componentStack: null !== stack ? stack : "" - }); - }); - return lane; -} var PossiblyWeakSet = "function" === typeof WeakSet ? WeakSet : Set, - nextEffect = null; + nextEffect = null, + inProgressLanes = null, + inProgressRoot = null; function safelyDetachRef(current, nearestMountedAncestor) { var ref = current.ref; if (null !== ref) if ("function" === typeof ref) try { - ref(null); + if (current.mode & 2) + try { + startLayoutEffectTimer(), ref(null); + } finally { + recordLayoutEffectDuration(current); + } + else ref(null); } catch (refError) { captureCommitPhaseError(current, nearestMountedAncestor, refError); } else ref.current = null; } +function safelyCallDestroy(current, nearestMountedAncestor, destroy) { + try { + destroy(); + } catch (error) { + captureCommitPhaseError(current, nearestMountedAncestor, error); + } +} var focusedInstanceHandle = null, shouldFireAfterActiveInstanceBlur = !1; function commitBeforeMutationEffects(root, firstChild) { @@ -5708,7 +5801,7 @@ function commitBeforeMutationEffects(root, firstChild) { function commitHookEffectListUnmount( flags, finishedWork, - nearestMountedAncestor$jscomp$0 + nearestMountedAncestor ) { var updateQueue = finishedWork.updateQueue; updateQueue = null !== updateQueue ? updateQueue.lastEffect : null; @@ -5718,15 +5811,8 @@ function commitHookEffectListUnmount( if ((effect.tag & flags) === flags) { var destroy = effect.destroy; effect.destroy = void 0; - if (void 0 !== destroy) { - var current = finishedWork, - nearestMountedAncestor = nearestMountedAncestor$jscomp$0; - try { - destroy(); - } catch (error) { - captureCommitPhaseError(current, nearestMountedAncestor, error); - } - } + void 0 !== destroy && + safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy); } effect = effect.next; } while (effect !== updateQueue); @@ -5739,8 +5825,8 @@ function commitHookEffectListMount(tag, finishedWork) { var effect = (finishedWork = finishedWork.next); do { if ((effect.tag & tag) === tag) { - var create$86 = effect.create; - effect.destroy = create$86(); + var create$88 = effect.create; + effect.destroy = create$88(); } effect = effect.next; } while (effect !== finishedWork); @@ -5766,7 +5852,14 @@ function commitWork(current, finishedWork) { case 11: case 14: case 15: - commitHookEffectListUnmount(3, finishedWork, finishedWork.return); + if (finishedWork.mode & 2) + try { + startLayoutEffectTimer(), + commitHookEffectListUnmount(3, finishedWork, finishedWork.return); + } finally { + recordLayoutEffectDuration(finishedWork); + } + else commitHookEffectListUnmount(3, finishedWork, finishedWork.return); return; case 12: return; @@ -5806,18 +5899,33 @@ function attachSuspenseRetryListeners(finishedWork) { (retryCache = finishedWork.stateNode = new PossiblyWeakSet()); wakeables.forEach(function(wakeable) { var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable); - retryCache.has(wakeable) || - (retryCache.add(wakeable), wakeable.then(retry, retry)); + if (!retryCache.has(wakeable)) { + retryCache.add(wakeable); + if (isDevToolsPresent) + if (null !== inProgressLanes && null !== inProgressRoot) + restorePendingUpdaters(inProgressRoot, inProgressLanes); + else + throw Error( + "Expected finished root and lanes to be set. This is a bug in React." + ); + wakeable.then(retry, retry); + } }); } } -function commitMutationEffects(root, firstChild) { +function commitMutationEffects(root, firstChild, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; for (nextEffect = firstChild; null !== nextEffect; ) { root = nextEffect; firstChild = root.deletions; if (null !== firstChild) - for (var i = 0; i < firstChild.length; i++) { - var childToDelete = firstChild[i]; + for ( + committedLanes = 0; + committedLanes < firstChild.length; + committedLanes++ + ) { + var childToDelete = firstChild[committedLanes]; try { a: for (var node = childToDelete; ; ) { var current = node; @@ -5843,19 +5951,13 @@ function commitMutationEffects(root, firstChild) { var _effect = effect, destroy = _effect.destroy, tag = _effect.tag; - if (void 0 !== destroy && 0 !== (tag & 2)) { - _effect = current; - var nearestMountedAncestor = root; - try { - destroy(); - } catch (error) { - captureCommitPhaseError( - _effect, - nearestMountedAncestor, - error - ); - } - } + void 0 !== destroy && + 0 !== (tag & 2) && + (current.mode & 2 + ? (startLayoutEffectTimer(), + safelyCallDestroy(current, root, destroy), + recordLayoutEffectDuration(current)) + : safelyCallDestroy(current, root, destroy)); effect = effect.next; } while (effect !== firstEffect); } @@ -5866,11 +5968,20 @@ function commitMutationEffects(root, firstChild) { var instance = current.stateNode; if ("function" === typeof instance.componentWillUnmount) try { - (effect = current), + if ( + ((effect = current), (_effect = instance), (_effect.props = effect.memoizedProps), (_effect.state = effect.memoizedState), - _effect.componentWillUnmount(); + effect.mode & 2) + ) + try { + startLayoutEffectTimer(), + _effect.componentWillUnmount(); + } finally { + recordLayoutEffectDuration(effect); + } + else _effect.componentWillUnmount(); } catch (unmountError) { captureCommitPhaseError(current, root, unmountError); } @@ -5912,11 +6023,18 @@ function commitMutationEffects(root, firstChild) { if (flags & 256) { var current$jscomp$0 = root.alternate; if (null !== current$jscomp$0) { - var currentRef = current$jscomp$0.ref; - null !== currentRef && - ("function" === typeof currentRef - ? currentRef(null) - : (currentRef.current = null)); + firstChild = current$jscomp$0; + var currentRef = firstChild.ref; + if (null !== currentRef) + if ("function" === typeof currentRef) + if (firstChild.mode & 2) + try { + startLayoutEffectTimer(), currentRef(null); + } finally { + recordLayoutEffectDuration(firstChild); + } + else currentRef(null); + else currentRef.current = null; } } switch (flags & 2054) { @@ -5949,83 +6067,145 @@ function commitMutationEffects(root, firstChild) { nextEffect = root.return; } } + inProgressRoot = inProgressLanes = null; } -function commitLayoutEffects(finishedWork) { - for (nextEffect = finishedWork; null !== nextEffect; ) { - var fiber = nextEffect, - firstChild = fiber.child; - if (0 !== (fiber.subtreeFlags & 324) && null !== firstChild) - (firstChild.return = fiber), (nextEffect = firstChild); +function commitLayoutEffects(finishedWork, root, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; + for (nextEffect = finishedWork; null !== nextEffect; ) + if ( + ((root = nextEffect), + (committedLanes = root.child), + 0 !== (root.subtreeFlags & 324) && null !== committedLanes) + ) + (committedLanes.return = root), (nextEffect = committedLanes); else - for (fiber = finishedWork; null !== nextEffect; ) { - firstChild = nextEffect; - if (0 !== (firstChild.flags & 324)) { - var current = firstChild.alternate; + for (root = finishedWork; null !== nextEffect; ) { + committedLanes = nextEffect; + if (0 !== (committedLanes.flags & 324)) { + var current = committedLanes.alternate; try { - if (0 !== (firstChild.flags & 68)) - switch (firstChild.tag) { + if (0 !== (committedLanes.flags & 68)) + switch (committedLanes.tag) { case 0: case 11: case 15: - commitHookEffectListMount(3, firstChild); + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + commitHookEffectListMount(3, committedLanes); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else commitHookEffectListMount(3, committedLanes); break; case 1: - var instance = firstChild.stateNode; - if (firstChild.flags & 4) - if (null === current) instance.componentDidMount(); + var instance = committedLanes.stateNode; + if (committedLanes.flags & 4) + if (null === current) + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + instance.componentDidMount(); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else instance.componentDidMount(); else { var prevProps = - firstChild.elementType === firstChild.type - ? current.memoizedProps - : resolveDefaultProps( - firstChild.type, - current.memoizedProps + committedLanes.elementType === committedLanes.type + ? current.memoizedProps + : resolveDefaultProps( + committedLanes.type, + current.memoizedProps + ), + prevState = current.memoizedState; + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate ); - instance.componentDidUpdate( - prevProps, - current.memoizedState, - instance.__reactInternalSnapshotBeforeUpdate - ); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate + ); } - var updateQueue = firstChild.updateQueue; + var updateQueue = committedLanes.updateQueue; null !== updateQueue && - commitUpdateQueue(firstChild, updateQueue, instance); + commitUpdateQueue(committedLanes, updateQueue, instance); break; case 3: - var updateQueue$87 = firstChild.updateQueue; - if (null !== updateQueue$87) { - var instance$88 = null; - if (null !== firstChild.child) - switch (firstChild.child.tag) { + var updateQueue$90 = committedLanes.updateQueue; + if (null !== updateQueue$90) { + var instance$91 = null; + if (null !== committedLanes.child) + switch (committedLanes.child.tag) { case 5: - instance$88 = firstChild.child.stateNode.canonical; + instance$91 = + committedLanes.child.stateNode.canonical; break; case 1: - instance$88 = firstChild.child.stateNode; + instance$91 = committedLanes.child.stateNode; } - commitUpdateQueue(firstChild, updateQueue$87, instance$88); + commitUpdateQueue( + committedLanes, + updateQueue$90, + instance$91 + ); } break; case 5: - null === current && firstChild.flags & 4 && shim(); + null === current && committedLanes.flags & 4 && shim(); break; case 6: break; case 4: break; case 12: - var onRender = firstChild.memoizedProps.onRender; - instance$88 = commitTime; + var _finishedWork$memoize2 = committedLanes.memoizedProps, + onCommit = _finishedWork$memoize2.onCommit, + onRender = _finishedWork$memoize2.onRender, + effectDuration = committedLanes.stateNode.effectDuration; + instance$91 = commitTime; current = null === current ? "mount" : "update"; + currentUpdateIsNested && (current = "nested-update"); "function" === typeof onRender && onRender( - firstChild.memoizedProps.id, + committedLanes.memoizedProps.id, + current, + committedLanes.actualDuration, + committedLanes.treeBaseDuration, + committedLanes.actualStartTime, + instance$91 + ); + "function" === typeof onCommit && + onCommit( + committedLanes.memoizedProps.id, current, - firstChild.actualDuration, - firstChild.treeBaseDuration, - firstChild.actualStartTime, - instance$88 + effectDuration, + instance$91 ); + enqueuePendingPassiveProfilerEffect(committedLanes); + var parentFiber = committedLanes.return; + a: for (; null !== parentFiber; ) { + switch (parentFiber.tag) { + case 3: + parentFiber.stateNode.effectDuration += effectDuration; + break a; + case 12: + parentFiber.stateNode.effectDuration += effectDuration; + break a; + } + parentFiber = parentFiber.return; + } break; case 13: break; @@ -6040,40 +6220,51 @@ function commitLayoutEffects(finishedWork) { "This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue." ); } - if (firstChild.flags & 256) { - instance$88 = void 0; - var ref = firstChild.ref; + if (committedLanes.flags & 256) { + instance$91 = void 0; + current = committedLanes; + var ref = current.ref; if (null !== ref) { - var instance$jscomp$0 = firstChild.stateNode; - switch (firstChild.tag) { + var instance$jscomp$0 = current.stateNode; + switch (current.tag) { case 5: - instance$88 = instance$jscomp$0.canonical; + instance$91 = instance$jscomp$0.canonical; break; default: - instance$88 = instance$jscomp$0; + instance$91 = instance$jscomp$0; } - "function" === typeof ref - ? ref(instance$88) - : (ref.current = instance$88); + if ("function" === typeof ref) + if (current.mode & 2) + try { + startLayoutEffectTimer(), ref(instance$91); + } finally { + recordLayoutEffectDuration(current); + } + else ref(instance$91); + else ref.current = instance$91; } } } catch (error) { - captureCommitPhaseError(firstChild, firstChild.return, error); + captureCommitPhaseError( + committedLanes, + committedLanes.return, + error + ); } } - if (firstChild === fiber) { + if (committedLanes === root) { nextEffect = null; break; } - instance$88 = firstChild.sibling; - if (null !== instance$88) { - instance$88.return = firstChild.return; - nextEffect = instance$88; + instance$91 = committedLanes.sibling; + if (null !== instance$91) { + instance$91.return = committedLanes.return; + nextEffect = instance$91; break; } - nextEffect = firstChild.return; + nextEffect = committedLanes.return; } - } + inProgressRoot = inProgressLanes = null; } var ceil = Math.ceil, ReactCurrentDispatcher$2 = ReactSharedInternals.ReactCurrentDispatcher, @@ -6098,6 +6289,7 @@ var ceil = Math.ceil, rootDoesHavePassiveEffects = !1, rootWithPendingPassiveEffects = null, pendingPassiveEffectsLanes = 0, + pendingPassiveProfilerEffects = [], nestedUpdateCount = 0, rootWithNestedUpdates = null, currentEventTime = -1, @@ -6111,6 +6303,8 @@ function requestEventTime() { } function requestUpdateLane(fiber) { if (0 === (fiber.mode & 1)) return 1; + if (0 !== (executionContext & 8) && 0 !== workInProgressRootRenderLanes) + return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; if (0 !== ReactCurrentBatchConfig.transition) return ( 0 === currentEventTransitionLane && @@ -6132,9 +6326,10 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { )); var root = markUpdateLaneFromFiberToRoot(fiber, lane); if (null === root) return null; + isDevToolsPresent && addFiberToLanesMap(root, fiber, lane); markRootUpdated(root, lane, eventTime); root === workInProgressRoot && - ((workInProgressRootUpdatedLanes |= lane), + (0 === (executionContext & 8) && (workInProgressRootUpdatedLanes |= lane), 4 === workInProgressRootExitStatus && markRootSuspended$1(root, workInProgressRootRenderLanes)); 1 === lane @@ -6233,6 +6428,7 @@ function ensureRootIsScheduled(root, currentTime) { } } function performConcurrentWorkOnRoot(root, didTimeout) { + nestedUpdateScheduled = currentUpdateIsNested = !1; currentEventTime = -1; currentEventTransitionLane = 0; if (0 !== (executionContext & 24)) @@ -6255,9 +6451,17 @@ function performConcurrentWorkOnRoot(root, didTimeout) { if ( workInProgressRoot !== root || workInProgressRootRenderLanes !== didTimeout - ) - (workInProgressRootRenderTargetTime = now() + 500), - prepareFreshStack(root, didTimeout); + ) { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + 0 < memoizedUpdaters.size && + (restorePendingUpdaters(root, workInProgressRootRenderLanes), + memoizedUpdaters.clear()); + movePendingFibersToMemoized(root, didTimeout); + } + workInProgressRootRenderTargetTime = now() + 500; + prepareFreshStack(root, didTimeout); + } do try { workLoopConcurrent(); @@ -6324,14 +6528,13 @@ function performConcurrentWorkOnRoot(root, didTimeout) { markRootSuspended$1(root, lanes); if ((lanes & 4194240) === lanes) break; didTimeout = root.eventTimes; - for (JSCompiler_inline_result = -1; 0 < lanes; ) { - var index$4 = 31 - clz32(lanes); - prevDispatcher = 1 << index$4; - index$4 = didTimeout[index$4]; - index$4 > JSCompiler_inline_result && - (JSCompiler_inline_result = index$4); - lanes &= ~prevDispatcher; - } + for (JSCompiler_inline_result = -1; 0 < lanes; ) + (memoizedUpdaters = 31 - clz32(lanes)), + (prevDispatcher = 1 << memoizedUpdaters), + (memoizedUpdaters = didTimeout[memoizedUpdaters]), + memoizedUpdaters > JSCompiler_inline_result && + (JSCompiler_inline_result = memoizedUpdaters), + (lanes &= ~prevDispatcher); lanes = JSCompiler_inline_result; lanes = now() - lanes; lanes = @@ -6382,6 +6585,8 @@ function markRootSuspended$1(root, suspendedLanes) { } } function performSyncWorkOnRoot(root) { + currentUpdateIsNested = nestedUpdateScheduled; + nestedUpdateScheduled = !1; if (0 !== (executionContext & 24)) throw Error("Should not already be working."); flushPassiveEffects(); @@ -6517,6 +6722,7 @@ function handleError(root$jscomp$0, thrownValue) { value = thrownValue; thrownValue = workInProgressRootRenderLanes; sourceFiber.flags |= 8192; + isDevToolsPresent && restorePendingUpdaters(root, thrownValue); if ( null !== value && "object" === typeof value && @@ -6538,15 +6744,15 @@ function handleError(root$jscomp$0, thrownValue) { } var hasInvisibleParentBoundary = 0 !== (suspenseStackCursor.current & 1), - workInProgress$81 = returnFiber; + workInProgress$32 = returnFiber; do { var JSCompiler_temp; - if ((JSCompiler_temp = 13 === workInProgress$81.tag)) { - var nextState = workInProgress$81.memoizedState; + if ((JSCompiler_temp = 13 === workInProgress$32.tag)) { + var nextState = workInProgress$32.memoizedState; if (null !== nextState) JSCompiler_temp = null !== nextState.dehydrated ? !0 : !1; else { - var props = workInProgress$81.memoizedProps; + var props = workInProgress$32.memoizedProps; JSCompiler_temp = void 0 === props.fallback ? !1 @@ -6558,17 +6764,17 @@ function handleError(root$jscomp$0, thrownValue) { } } if (JSCompiler_temp) { - var wakeables = workInProgress$81.updateQueue; + var wakeables = workInProgress$32.updateQueue; if (null === wakeables) { var updateQueue = new Set(); updateQueue.add(wakeable); - workInProgress$81.updateQueue = updateQueue; + workInProgress$32.updateQueue = updateQueue; } else wakeables.add(wakeable); if ( - 0 === (workInProgress$81.mode & 1) && - workInProgress$81 !== returnFiber + 0 === (workInProgress$32.mode & 1) && + workInProgress$32 !== returnFiber ) { - workInProgress$81.flags |= 128; + workInProgress$32.flags |= 128; sourceFiber.flags |= 32768; sourceFiber.flags &= -10053; if (1 === sourceFiber.tag) @@ -6599,14 +6805,15 @@ function handleError(root$jscomp$0, thrownValue) { wakeable, sourceFiber ); + isDevToolsPresent && restorePendingUpdaters(root, sourceFiber); wakeable.then(ping, ping); } - workInProgress$81.flags |= 16384; - workInProgress$81.lanes = thrownValue; + workInProgress$32.flags |= 16384; + workInProgress$32.lanes = thrownValue; break a; } - workInProgress$81 = workInProgress$81.return; - } while (null !== workInProgress$81); + workInProgress$32 = workInProgress$32.return; + } while (null !== workInProgress$32); value = Error( (getComponentNameFromFiber(sourceFiber) || "A React component") + " suspended while rendering, but no fallback UI was specified.\n\nAdd a component higher in the tree to provide a loading indicator or placeholder to display." @@ -6615,47 +6822,47 @@ function handleError(root$jscomp$0, thrownValue) { 5 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2); value = createCapturedValue(value, sourceFiber); - workInProgress$81 = returnFiber; + workInProgress$32 = returnFiber; do { - switch (workInProgress$81.tag) { + switch (workInProgress$32.tag) { case 3: root = value; - workInProgress$81.flags |= 16384; + workInProgress$32.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$81.lanes |= thrownValue; - var update$82 = createRootErrorUpdate( - workInProgress$81, + workInProgress$32.lanes |= thrownValue; + var update$33 = createRootErrorUpdate( + workInProgress$32, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$81, update$82); + enqueueCapturedUpdate(workInProgress$32, update$33); break a; case 1: root = value; - var ctor = workInProgress$81.type, - instance = workInProgress$81.stateNode; + var ctor = workInProgress$32.type, + instance = workInProgress$32.stateNode; if ( - 0 === (workInProgress$81.flags & 128) && + 0 === (workInProgress$32.flags & 128) && ("function" === typeof ctor.getDerivedStateFromError || (null !== instance && "function" === typeof instance.componentDidCatch && (null === legacyErrorBoundariesThatAlreadyFailed || !legacyErrorBoundariesThatAlreadyFailed.has(instance)))) ) { - workInProgress$81.flags |= 16384; + workInProgress$32.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$81.lanes |= thrownValue; - var update$85 = createClassErrorUpdate( - workInProgress$81, + workInProgress$32.lanes |= thrownValue; + var update$36 = createClassErrorUpdate( + workInProgress$32, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$81, update$85); + enqueueCapturedUpdate(workInProgress$32, update$36); break a; } } - workInProgress$81 = workInProgress$81.return; - } while (null !== workInProgress$81); + workInProgress$32 = workInProgress$32.return; + } while (null !== workInProgress$32); } completeUnitOfWork(erroredWork); } catch (yetAnotherThrownValue) { @@ -6677,8 +6884,16 @@ function renderRootSync(root, lanes) { var prevExecutionContext = executionContext; executionContext |= 8; var prevDispatcher = pushDispatcher(); - (workInProgressRoot === root && workInProgressRootRenderLanes === lanes) || + if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + 0 < memoizedUpdaters.size && + (restorePendingUpdaters(root, workInProgressRootRenderLanes), + memoizedUpdaters.clear()); + movePendingFibersToMemoized(root, lanes); + } prepareFreshStack(root, lanes); + } do try { workLoopSync(); @@ -6819,7 +7034,7 @@ function commitRootImpl(root, renderPriorityLevel) { ReactCurrentOwner$2.current = null; commitBeforeMutationEffects(root, finishedWork); commitTime = now$1(); - commitMutationEffects(root, finishedWork); + commitMutationEffects(root, finishedWork, lanes); root.current = finishedWork; commitLayoutEffects(finishedWork, root, lanes); requestPaint(); @@ -6834,11 +7049,13 @@ function commitRootImpl(root, renderPriorityLevel) { remainingLanes = root.pendingLanes; 0 === remainingLanes && (legacyErrorBoundariesThatAlreadyFailed = null); 0 !== (remainingLanes & 1) - ? root === rootWithNestedUpdates - ? nestedUpdateCount++ - : ((nestedUpdateCount = 0), (rootWithNestedUpdates = root)) + ? ((nestedUpdateScheduled = !0), + root === rootWithNestedUpdates + ? nestedUpdateCount++ + : ((nestedUpdateCount = 0), (rootWithNestedUpdates = root))) : (nestedUpdateCount = 0); onCommitRoot(finishedWork.stateNode, renderPriorityLevel); + isDevToolsPresent && root.memoizedUpdaters.clear(); ensureRootIsScheduled(root, now()); if (hasUncaughtError) throw ((hasUncaughtError = !1), @@ -6879,12 +7096,17 @@ function flushPassiveEffects() { for (var i = 0; i < deletions.length; i++) { var fiberToDelete = deletions[i]; for (nextEffect = fiberToDelete; null !== nextEffect; ) { - var fiber$jscomp$0 = nextEffect; - switch (fiber$jscomp$0.tag) { + var fiber$jscomp$0 = nextEffect, + current = fiber$jscomp$0; + switch (current.tag) { case 0: case 11: case 15: - commitHookEffectListUnmount(4, fiber$jscomp$0, fiber); + current.mode & 2 + ? ((passiveEffectStartTime = now$1()), + commitHookEffectListUnmount(4, current, fiber), + recordPassiveEffectDuration(current)) + : commitHookEffectListUnmount(4, current, fiber); } var child$jscomp$0 = fiber$jscomp$0.child; if (null !== child$jscomp$0) @@ -6930,11 +7152,15 @@ function flushPassiveEffects() { b: for (; null !== nextEffect; ) { fiber = nextEffect; if (0 !== (fiber.flags & 1024)) - switch (fiber.tag) { + switch (((i = fiber), i.tag)) { case 0: case 11: case 15: - commitHookEffectListUnmount(5, fiber, fiber.return); + i.mode & 2 + ? ((passiveEffectStartTime = now$1()), + commitHookEffectListUnmount(5, i, i.return), + recordPassiveEffectDuration(i)) + : commitHookEffectListUnmount(5, i, i.return); } var sibling$jscomp$0 = fiber.sibling; if (null !== sibling$jscomp$0) { @@ -6956,11 +7182,18 @@ function flushPassiveEffects() { deletions = nextEffect; if (0 !== (deletions.flags & 1024)) try { - switch (deletions.tag) { + switch (((fiberToDelete = deletions), fiberToDelete.tag)) { case 0: case 11: case 15: - commitHookEffectListMount(5, deletions); + if (fiberToDelete.mode & 2) { + passiveEffectStartTime = now$1(); + try { + commitHookEffectListMount(5, fiberToDelete); + } finally { + recordPassiveEffectDuration(fiberToDelete); + } + } else commitHookEffectListMount(5, fiberToDelete); } } catch (error) { captureCommitPhaseError(deletions, deletions.return, error); @@ -6978,6 +7211,43 @@ function flushPassiveEffects() { nextEffect = deletions.return; } } + finishedWork = pendingPassiveProfilerEffects; + pendingPassiveProfilerEffects = []; + for (firstChild = 0; firstChild < finishedWork.length; firstChild++) { + var finishedWork$jscomp$0 = finishedWork[firstChild]; + if (0 !== (finishedWork$jscomp$0.flags & 4)) + switch (finishedWork$jscomp$0.tag) { + case 12: + var passiveEffectDuration = + finishedWork$jscomp$0.stateNode.passiveEffectDuration, + _finishedWork$memoize = finishedWork$jscomp$0.memoizedProps, + id = _finishedWork$memoize.id, + onPostCommit = _finishedWork$memoize.onPostCommit; + sibling$jscomp$1 = commitTime; + var phase = + null === finishedWork$jscomp$0.alternate ? "mount" : "update"; + currentUpdateIsNested && (phase = "nested-update"); + "function" === typeof onPostCommit && + onPostCommit( + id, + phase, + passiveEffectDuration, + sibling$jscomp$1 + ); + var parentFiber = finishedWork$jscomp$0.return; + b: for (; null !== parentFiber; ) { + switch (parentFiber.tag) { + case 3: + parentFiber.stateNode.passiveEffectDuration += passiveEffectDuration; + break b; + case 12: + parentFiber.stateNode.passiveEffectDuration += passiveEffectDuration; + break b; + } + parentFiber = parentFiber.return; + } + } + } executionContext = prevExecutionContext; flushSyncCallbacks(); if ( @@ -6987,6 +7257,9 @@ function flushPassiveEffects() { try { injectedHook.onPostCommitFiberRoot(rendererID, renderPriority); } catch (err) {} + var stateNode = renderPriority.current.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; JSCompiler_inline_result = !0; } return JSCompiler_inline_result; @@ -6997,6 +7270,15 @@ function flushPassiveEffects() { } return !1; } +function enqueuePendingPassiveProfilerEffect(fiber) { + pendingPassiveProfilerEffects.push(fiber); + rootDoesHavePassiveEffects || + ((rootDoesHavePassiveEffects = !0), + scheduleCallback(NormalPriority, function() { + flushPassiveEffects(); + return null; + })); +} function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) { sourceFiber = createCapturedValue(error, sourceFiber); sourceFiber = createRootErrorUpdate(rootFiber, sourceFiber, 1); @@ -7123,6 +7405,9 @@ beginWork$1 = function(current, workInProgress, renderLanes) { case 12: 0 !== (renderLanes & workInProgress.childLanes) && (workInProgress.flags |= 4); + updateLanes = workInProgress.stateNode; + updateLanes.effectDuration = 0; + updateLanes.passiveEffectDuration = 0; break; case 13: if (null !== workInProgress.memoizedState) { @@ -7417,6 +7702,9 @@ beginWork$1 = function(current, workInProgress, renderLanes) { case 12: return ( (workInProgress.flags |= 4), + (updateLanes = workInProgress.stateNode), + (updateLanes.effectDuration = 0), + (updateLanes.passiveEffectDuration = 0), reconcileChildren( current, workInProgress, @@ -7596,6 +7884,12 @@ beginWork$1 = function(current, workInProgress, renderLanes) { "). This error is likely caused by a bug in React. Please file an issue." ); }; +function restorePendingUpdaters(root, lanes) { + isDevToolsPresent && + root.memoizedUpdaters.forEach(function(schedulingFiber) { + addFiberToLanesMap(root, schedulingFiber, lanes); + }); +} function FiberNode(tag, pendingProps, key, mode) { this.tag = tag; this.key = key; @@ -7692,7 +7986,7 @@ function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = 8; - mode |= 24; + mode |= 8; break; case REACT_PROFILER_TYPE: return ( @@ -7801,6 +8095,10 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.expirationTimes = createLaneMap(-1); this.entangledLanes = this.finishedLanes = this.mutableReadLanes = this.expiredLanes = this.pingedLanes = this.suspendedLanes = this.pendingLanes = 0; this.entanglements = createLaneMap(0); + this.passiveEffectDuration = this.effectDuration = 0; + this.memoizedUpdaters = new Set(); + containerInfo = this.pendingUpdatersLaneMap = []; + for (tag = 0; 31 > tag; tag++) containerInfo.push(new Set()); } function createPortal(children, containerInfo, implementation) { var key = @@ -7915,10 +8213,10 @@ batchedUpdatesImpl = function(fn, a) { } }; var roots = new Map(), - devToolsConfig$jscomp$inline_966 = { + devToolsConfig$jscomp$inline_972 = { findFiberByHostInstance: getInstanceFromInstance, bundleType: 0, - version: "17.0.3-experimental-2d8d133e1", + version: "17.0.3-experimental-0eea57724", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function() { @@ -7933,17 +8231,18 @@ var roots = new Map(), }.bind(null, findNodeHandle) } }; -var internals$jscomp$inline_1208 = { - bundleType: devToolsConfig$jscomp$inline_966.bundleType, - version: devToolsConfig$jscomp$inline_966.version, - rendererPackageName: devToolsConfig$jscomp$inline_966.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_966.rendererConfig, +var internals$jscomp$inline_1230 = { + bundleType: devToolsConfig$jscomp$inline_972.bundleType, + version: devToolsConfig$jscomp$inline_972.version, + rendererPackageName: devToolsConfig$jscomp$inline_972.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_972.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, overrideProps: null, overridePropsDeletePath: null, overridePropsRenamePath: null, + setErrorHandler: null, setSuspenseHandler: null, scheduleUpdate: null, currentDispatcherRef: ReactSharedInternals.ReactCurrentDispatcher, @@ -7952,26 +8251,26 @@ var internals$jscomp$inline_1208 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_966.findFiberByHostInstance || + devToolsConfig$jscomp$inline_972.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "17.0.3-experimental-2d8d133e1" + reconcilerVersion: "17.0.3-experimental-0eea57724" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_1209 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_1231 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_1209.isDisabled && - hook$jscomp$inline_1209.supportsFiber + !hook$jscomp$inline_1231.isDisabled && + hook$jscomp$inline_1231.supportsFiber ) try { - (rendererID = hook$jscomp$inline_1209.inject( - internals$jscomp$inline_1208 + (rendererID = hook$jscomp$inline_1231.inject( + internals$jscomp$inline_1230 )), - (injectedHook = hook$jscomp$inline_1209); + (injectedHook = hook$jscomp$inline_1231); } catch (err) {} } exports.createPortal = function(children, containerTag) { diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js b/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js index 42a94f230c31dd..2a912e5e98782c 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-dev.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<<2d7090219901967e7d2d5c87aacfd377>> + * @generated SignedSource<> */ 'use strict'; @@ -3134,7 +3134,6 @@ var enableProfilerTimer = true; var enableLazyElements = false; var warnAboutStringRefs = false; var enableNewReconciler = false; -var deferRenderPhaseUpdateToNextBatch = true; var enableLazyContextPropagation = false; // Don't change these two values. They're used by React Dev Tools. @@ -4940,6 +4939,48 @@ function markRootEntangled(root, entangledLanes) { lanes &= ~lane; } } +function addFiberToLanesMap(root, fiber, lanes) { + if (!isDevToolsPresent) { + return; + } + + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; + + while (lanes > 0) { + var index = laneToIndex(lanes); + var lane = 1 << index; + var updaters = pendingUpdatersLaneMap[index]; + updaters.add(fiber); + lanes &= ~lane; + } +} +function movePendingFibersToMemoized(root, lanes) { + if (!isDevToolsPresent) { + return; + } + + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; + var memoizedUpdaters = root.memoizedUpdaters; + + while (lanes > 0) { + var index = laneToIndex(lanes); + var lane = 1 << index; + var updaters = pendingUpdatersLaneMap[index]; + + if (updaters.size > 0) { + updaters.forEach(function(fiber) { + var alternate = fiber.alternate; + + if (alternate === null || !memoizedUpdaters.has(alternate)) { + memoizedUpdaters.add(fiber); + } + }); + updaters.clear(); + } + + lanes &= ~lane; + } +} var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros. Only used on lanes, so assume input is an integer. // Based on: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 @@ -6100,7 +6141,7 @@ var Passive$1 = /* */ 4; -var ReactVersion = "17.0.3-2d8d133e1"; +var ReactVersion = "17.0.3-0eea57724"; var ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig; var NoTransition = 0; @@ -6884,9 +6925,7 @@ function readContext(context) { lastContextDependency = contextItem; currentlyRenderingFiber.dependencies = { lanes: NoLanes, - firstContext: contextItem, - // TODO: This is an old field. Delete it. - responders: null + firstContext: contextItem }; } else { // Append a new context item. @@ -8280,7 +8319,6 @@ function mountClassInstance(workInProgress, ctor, newProps, renderLanes) { var fiberFlags = Update; if ((workInProgress.mode & StrictEffectsMode) !== NoMode) { - // Never double-invoke effects for legacy roots. fiberFlags |= MountLayoutDev; } @@ -8349,7 +8387,6 @@ function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) { var fiberFlags = Update; if ((workInProgress.mode & StrictEffectsMode) !== NoMode) { - // Never double-invoke effects for legacy roots. fiberFlags |= MountLayoutDev; } @@ -8402,7 +8439,6 @@ function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) { var _fiberFlags = Update; if ((workInProgress.mode & StrictEffectsMode) !== NoMode) { - // Never double-invoke effects for legacy roots. _fiberFlags |= MountLayoutDev; } @@ -8415,7 +8451,6 @@ function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) { var _fiberFlags2 = Update; if ((workInProgress.mode & StrictEffectsMode) !== NoMode) { - // Never double-invoke effects for legacy roots. _fiberFlags2 |= MountLayoutDev; } @@ -12578,7 +12613,52 @@ var InvalidNestedHooksDispatcherOnRerenderInDEV = null; var now$1 = Scheduler.unstable_now; var commitTime = 0; +var layoutEffectStartTime = -1; var profilerStartTime = -1; +var passiveEffectStartTime = -1; +/** + * Tracks whether the current update was a nested/cascading update (scheduled from a layout effect). + * + * The overall sequence is: + * 1. render + * 2. commit (and call `onRender`, `onCommit`) + * 3. check for nested updates + * 4. flush passive effects (and call `onPostCommit`) + * + * Nested updates are identified in step 3 above, + * but step 4 still applies to the work that was just committed. + * We use two flags to track nested updates then: + * one tracks whether the upcoming update is a nested update, + * and the other tracks whether the current update was a nested update. + * The first value gets synced to the second at the start of the render phase. + */ + +var currentUpdateIsNested = false; +var nestedUpdateScheduled = false; + +function isCurrentUpdateNested() { + return currentUpdateIsNested; +} + +function markNestedUpdateScheduled() { + { + nestedUpdateScheduled = true; + } +} + +function resetNestedUpdateFlag() { + { + currentUpdateIsNested = false; + nestedUpdateScheduled = false; + } +} + +function syncNestedUpdateFlag() { + { + currentUpdateIsNested = nestedUpdateScheduled; + nestedUpdateScheduled = false; + } +} function getCommitTime() { return commitTime; @@ -12613,6 +12693,77 @@ function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { } } +function recordLayoutEffectDuration(fiber) { + if (layoutEffectStartTime >= 0) { + var elapsedTime = now$1() - layoutEffectStartTime; + layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor + // Or the root (for the DevTools Profiler to read) + + var parentFiber = fiber.return; + + while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.effectDuration += elapsedTime; + return; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.effectDuration += elapsedTime; + return; + } + + parentFiber = parentFiber.return; + } + } +} + +function recordPassiveEffectDuration(fiber) { + if (passiveEffectStartTime >= 0) { + var elapsedTime = now$1() - passiveEffectStartTime; + passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor + // Or the root (for the DevTools Profiler to read) + + var parentFiber = fiber.return; + + while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + + if (root !== null) { + root.passiveEffectDuration += elapsedTime; + } + + return; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + + if (parentStateNode !== null) { + // Detached fibers have their state node cleared out. + // In this case, the return pointer is also cleared out, + // so we won't be able to report the time spent in this Profiler's subtree. + parentStateNode.passiveEffectDuration += elapsedTime; + } + + return; + } + + parentFiber = parentFiber.return; + } + } +} + +function startLayoutEffectTimer() { + layoutEffectStartTime = now$1(); +} + +function startPassiveEffectTimer() { + passiveEffectStartTime = now$1(); +} + function transferActualDuration(fiber) { // Transfer time spent rendering these children so we don't lose it // after we rerender. This is used as a helper in special cases @@ -12625,4217 +12776,4282 @@ function transferActualDuration(fiber) { } } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; -var didReceiveUpdate = false; -var didWarnAboutBadClass; -var didWarnAboutModulePatternComponent; -var didWarnAboutContextTypeOnFunctionComponent; -var didWarnAboutGetDerivedStateOnFunctionComponent; -var didWarnAboutFunctionRefs; -var didWarnAboutReassigningProps; -var didWarnAboutRevealOrder; -var didWarnAboutTailOptions; - -{ - didWarnAboutBadClass = {}; - didWarnAboutModulePatternComponent = {}; - didWarnAboutContextTypeOnFunctionComponent = {}; - didWarnAboutGetDerivedStateOnFunctionComponent = {}; - didWarnAboutFunctionRefs = {}; - didWarnAboutReassigningProps = false; - didWarnAboutRevealOrder = {}; - didWarnAboutTailOptions = {}; -} - -function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { - if (current === null) { - // If this is a fresh new component that hasn't been rendered yet, we - // won't update its child set by applying minimal side-effects. Instead, - // we will add them all to the child before it gets rendered. That means - // we can optimize this reconciliation pass by not tracking side-effects. - workInProgress.child = mountChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - } else { - // If the current child is the same as the work in progress, it means that - // we haven't yet started any work on these children. Therefore, we use - // the clone algorithm to create a copy of all the current children. - // If we had any progressed work already, that is invalid at this point so - // let's throw it out. - workInProgress.child = reconcileChildFibers( - workInProgress, - current.child, - nextChildren, - renderLanes - ); - } +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: getStackByFiberInDevAndProd(source) + }; } -function forceUnmountCurrentAndReconcile( - current, - workInProgress, - nextChildren, - renderLanes +if ( + !( + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog === + "function" + ) ) { - // This function is fork of reconcileChildren. It's used in cases where we - // want to reconcile without matching against the existing set. This has the - // effect of all current children being unmounted; even if the type and key - // are the same, the old child is unmounted and a new child is created. - // - // To do this, we're going to go through the reconcile algorithm twice. In - // the first pass, we schedule a deletion for all the current children by - // passing null. - workInProgress.child = reconcileChildFibers( - workInProgress, - current.child, - null, - renderLanes - ); // In the second pass, we mount the new children. The trick here is that we - // pass null in place of where we usually pass the current child set. This has - // the effect of remounting all children regardless of whether their - // identities match. + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +} - workInProgress.child = reconcileChildFibers( - workInProgress, - null, - nextChildren, - renderLanes +function showErrorDialog(boundary, errorInfo) { + var capturedError = { + componentStack: errorInfo.stack !== null ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + boundary !== null && boundary.tag === ClassComponent + ? boundary.stateNode + : null + }; + return ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog( + capturedError ); } -function updateForwardRef( - current, - workInProgress, - Component, - nextProps, - renderLanes -) { - // TODO: current can be non-null here even if the component - // hasn't yet mounted. This happens after the first render suspends. - // We'll need to figure out if this is fine or can cause issues. - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; +function logCapturedError(boundary, errorInfo) { + try { + var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. + // This enables renderers like ReactNative to better manage redbox behavior. - if (innerPropTypes) { - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(Component) - ); - } + if (logError === false) { + return; } - } - var render = Component.render; - var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent + var error = errorInfo.value; - var nextChildren; - prepareToReadContext(workInProgress, renderLanes); + if (true) { + var source = errorInfo.source; + var stack = errorInfo.stack; + var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling + // `preventDefault()` in window `error` handler. + // We record this information as an expando on the error. - { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - nextChildren = renderWithHooks( - current, - workInProgress, - render, - nextProps, - ref, - renderLanes - ); + if (error != null && error._suppressLogging) { + if (boundary.tag === ClassComponent) { + // The error is recoverable and was silenced. + // Ignore it and don't print the stack addendum. + // This is handy for testing error boundaries without noise. + return; + } // The error is fatal. Since the silencing might have + // been accidental, we'll surface it anyway. + // However, the browser would have silenced the original error + // so we'll print it first, and then print the stack addendum. - if (workInProgress.mode & StrictLegacyMode) { - disableLogs(); + console["error"](error); // Don't transform to our wrapper + // For a more detailed description of this block, see: + // https://github.com/facebook/react/pull/13384 + } - try { - nextChildren = renderWithHooks( - current, - workInProgress, - render, - nextProps, - ref, - renderLanes - ); - } finally { - reenableLogs(); + var componentName = source ? getComponentNameFromFiber(source) : null; + var componentNameMessage = componentName + ? "The above error occurred in the <" + componentName + "> component:" + : "The above error occurred in one of your React components:"; + var errorBoundaryMessage; + + if (boundary.tag === HostRoot) { + errorBoundaryMessage = + "Consider adding an error boundary to your tree to customize error handling behavior.\n" + + "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries."; + } else { + var errorBoundaryName = + getComponentNameFromFiber(boundary) || "Anonymous"; + errorBoundaryMessage = + "React will try to recreate this component tree from scratch " + + ("using the error boundary you provided, " + errorBoundaryName + "."); } - } - setIsRendering(false); + var combinedMessage = + componentNameMessage + + "\n" + + 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); // Don't transform to our wrapper + } else { + // In production, we print the error directly. + // This will include the message, the JS stack, and anything the browser wants to show. + // We pass the error object instead of custom message so that the browser displays the error natively. + console["error"](error); // Don't transform to our wrapper + } + } catch (e) { + // This method must not throw, or React internal state will get messed up. + // If console.error is overridden, or logCapturedError() shows a dialog that throws, + // we want to report this error outside of the normal stack as a last resort. + // https://github.com/facebook/react/issues/13188 + setTimeout(function() { + throw e; + }); } +} - if (current !== null && !didReceiveUpdate) { - bailoutHooks(current, workInProgress, renderLanes); - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } // React DevTools reads this flag. +var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; +function createRootErrorUpdate(fiber, errorInfo, lane) { + var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. + + update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property + // being called "element". + + update.payload = { + element: null + }; + var error = errorInfo.value; + + update.callback = function() { + onUncaughtError(error); + logCapturedError(fiber, errorInfo); + }; + + return update; } -function updateMemoComponent( - current, - workInProgress, - Component, - nextProps, - updateLanes, - renderLanes -) { - if (current === null) { - var type = Component.type; +function createClassErrorUpdate(fiber, errorInfo, lane) { + var update = createUpdate(NoTimestamp, lane); + update.tag = CaptureUpdate; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ( - isSimpleFunctionComponent(type) && - Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. - Component.defaultProps === undefined - ) { - var resolvedType = type; + if (typeof getDerivedStateFromError === "function") { + var error$1 = errorInfo.value; - { - resolvedType = resolveFunctionForHotReloading(type); - } // If this is a plain function component without default props, - // and with only the default shallow comparison, we upgrade it - // to a SimpleMemoComponent to allow fast path updates. + update.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error$1); + }; + } - workInProgress.tag = SimpleMemoComponent; - workInProgress.type = resolvedType; + var inst = fiber.stateNode; + if (inst !== null && typeof inst.componentDidCatch === "function") { + update.callback = function callback() { { - validateFunctionComponentInDev(workInProgress, type); + markFailedErrorBoundaryForHotReloading(fiber); } - return updateSimpleMemoComponent( - current, - workInProgress, - resolvedType, - nextProps, - updateLanes, - renderLanes - ); - } - - { - var innerPropTypes = type.propTypes; + if (typeof getDerivedStateFromError !== "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 getDerivedStateFromError is + // not defined. + markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined - if (innerPropTypes) { - // Inner memo component props aren't currently validated in createElement. - // We could move it there, but we'd still need this for lazy code path. - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(type) - ); + logCapturedError(fiber, errorInfo); } - } - var child = createFiberFromTypeAndProps( - Component.type, - null, - nextProps, - workInProgress, - workInProgress.mode, - renderLanes - ); - child.ref = workInProgress.ref; - child.return = workInProgress; - workInProgress.child = child; - return child; + var error$1 = errorInfo.value; + var stack = errorInfo.stack; + this.componentDidCatch(error$1, { + componentStack: stack !== null ? stack : "" + }); + + { + if (typeof getDerivedStateFromError !== "function") { + // If componentDidCatch is the only error boundary method defined, + // then it needs to call setState to recover from errors. + // If no state update is scheduled then the boundary will swallow the error. + if (!includesSomeLane(fiber.lanes, SyncLane)) { + error( + "%s: Error boundaries should implement getDerivedStateFromError(). " + + "In that method, return a state update to display an error message or fallback UI.", + getComponentNameFromFiber(fiber) || "Unknown" + ); + } + } + } + }; + } else { + update.callback = function() { + markFailedErrorBoundaryForHotReloading(fiber); + }; } - { - var _type = Component.type; - var _innerPropTypes = _type.propTypes; + return update; +} - if (_innerPropTypes) { - // Inner memo component props aren't currently validated in createElement. - // We could move it there, but we'd still need this for lazy code path. - checkPropTypes( - _innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(_type) - ); - } - } +function attachPingListener(root, wakeable, lanes) { + // Attach a listener to the promise to "ping" the root and retry. But only if + // one does not already exist for the lanes we're currently rendering (which + // acts like a "thread ID" here). + var pingCache = root.pingCache; + var threadIDs; - var currentChild = current.child; // This is always exactly one child + if (pingCache === null) { + pingCache = root.pingCache = new PossiblyWeakMap$1(); + threadIDs = new Set(); + pingCache.set(wakeable, threadIDs); + } else { + threadIDs = pingCache.get(wakeable); - if (!includesSomeLane(updateLanes, renderLanes)) { - // This will be the props with resolved defaultProps, - // unlike current.memoizedProps which will be the unresolved ones. - var prevProps = currentChild.memoizedProps; // Default to shallow comparison + if (threadIDs === undefined) { + threadIDs = new Set(); + pingCache.set(wakeable, threadIDs); + } + } - var compare = Component.compare; - compare = compare !== null ? compare : shallowEqual; + if (!threadIDs.has(lanes)) { + // Memoize using the thread ID to prevent redundant listeners. + threadIDs.add(lanes); + var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); - if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + { + if (isDevToolsPresent) { + // If we have pending work still, restore the original updaters + restorePendingUpdaters(root, lanes); + } } - } // React DevTools reads this flag. - workInProgress.flags |= PerformedWork; - var newChild = createWorkInProgress(currentChild, nextProps); - newChild.ref = workInProgress.ref; - newChild.return = workInProgress; - workInProgress.child = newChild; - return newChild; + wakeable.then(ping, ping); + } } -function updateSimpleMemoComponent( - current, - workInProgress, - Component, - nextProps, - updateLanes, - renderLanes +function throwException( + root, + returnFiber, + sourceFiber, + value, + rootRenderLanes ) { - // TODO: current can be non-null here even if the component - // hasn't yet mounted. This happens when the inner render suspends. - // We'll need to figure out if this is fine or can cause issues. + // The source fiber did not complete. + sourceFiber.flags |= Incomplete; + { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var outerMemoType = workInProgress.elementType; - - if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { - // We warn when you define propTypes on lazy() - // so let's just skip over it to find memo() outer wrapper. - // Inner props for memo are validated later. - var lazyComponent = outerMemoType; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - - try { - outerMemoType = init(payload); - } catch (x) { - outerMemoType = null; - } // Inner propTypes will be validated in the function component path. - - var outerPropTypes = outerMemoType && outerMemoType.propTypes; - - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - nextProps, // Resolved (SimpleMemoComponent has no defaultProps) - "prop", - getComponentNameFromType(outerMemoType) - ); - } - } - } - } - - if (current !== null) { - var prevProps = current.memoizedProps; - - if ( - shallowEqual(prevProps, nextProps) && - current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload. - workInProgress.type === current.type - ) { - didReceiveUpdate = false; - - if (!includesSomeLane(renderLanes, updateLanes)) { - // The pending lanes were cleared at the beginning of beginWork. We're - // about to bail out, but there might be other lanes that weren't - // included in the current render. Usually, the priority level of the - // remaining updates is accumlated during the evaluation of the - // component (i.e. when processing the update queue). But since since - // we're bailing out early *without* evaluating the component, we need - // to account for it here, too. Reset to the value of the current fiber. - // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, - // because a MemoComponent fiber does not have hooks or an update queue; - // rather, it wraps around an inner component, which may or may not - // contains hooks. - // TODO: Move the reset at in beginWork out of the common path so that - // this is no longer necessary. - workInProgress.lanes = current.lanes; - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); - } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { - // This is a special case that only exists for legacy mode. - // See https://github.com/facebook/react/pull/19216. - didReceiveUpdate = true; - } - } - } - - return updateFunctionComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes - ); -} - -function updateOffscreenComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; - var nextChildren = nextProps.children; - var prevState = current !== null ? current.memoizedState : null; // If this is not null, this is a cache pool that was carried over from the - // previous render. We will push this to the cache pool context so that we can - // resume in-flight requests. - - var spawnedCachePool = null; - - if ( - nextProps.mode === "hidden" || - nextProps.mode === "unstable-defer-without-hiding" - ) { - // Rendering a hidden tree. - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - // In legacy sync mode, don't defer the subtree. Render it now. - var nextState = { - baseLanes: NoLanes, - cachePool: null - }; - workInProgress.memoizedState = nextState; - pushRenderLanes(workInProgress, renderLanes); - } else if (!includesSomeLane(renderLanes, OffscreenLane)) { - // We're hidden, and we're not rendering at Offscreen. We will bail out - // and resume this tree later. - var nextBaseLanes; - - if (prevState !== null) { - var prevBaseLanes = prevState.baseLanes; - nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); - } else { - nextBaseLanes = renderLanes; - } // Schedule this fiber to re-render at offscreen priority. Then bailout. - - workInProgress.lanes = workInProgress.childLanes = laneToLanes( - OffscreenLane - ); - var _nextState = { - baseLanes: nextBaseLanes, - cachePool: spawnedCachePool - }; - workInProgress.memoizedState = _nextState; - workInProgress.updateQueue = null; // We're about to bail out, but we need to push this to the stack anyway - // to avoid a push/pop misalignment. - - pushRenderLanes(workInProgress, nextBaseLanes); - - return null; - } else { - var _nextState2 = { - baseLanes: NoLanes, - cachePool: null - }; - workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. - - var subtreeRenderLanes = - prevState !== null ? prevState.baseLanes : renderLanes; - pushRenderLanes(workInProgress, subtreeRenderLanes); - } - } else { - // Rendering a visible tree. - var _subtreeRenderLanes; - - if (prevState !== null) { - // We're going from hidden -> visible. - _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); - - workInProgress.memoizedState = null; - } else { - // We weren't previously hidden, and we still aren't, so there's nothing - // special to do. Need to push to the stack regardless, though, to avoid - // a push/pop misalignment. - _subtreeRenderLanes = renderLanes; - } - - pushRenderLanes(workInProgress, _subtreeRenderLanes); - } - - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} // Note: These happen to have identical begin phases, for now. We shouldn't hold -// ourselves to this constraint, though. If the behavior diverges, we should -// fork the function. - -var updateLegacyHiddenComponent = updateOffscreenComponent; - -function updateFragment(current, workInProgress, renderLanes) { - var nextChildren = workInProgress.pendingProps; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} - -function updateMode(current, workInProgress, renderLanes) { - var nextChildren = workInProgress.pendingProps.children; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} - -function updateProfiler(current, workInProgress, renderLanes) { - { - workInProgress.flags |= Update; - } - - var nextProps = workInProgress.pendingProps; - var nextChildren = nextProps.children; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} - -function markRef(current, workInProgress) { - var ref = workInProgress.ref; - - if ( - (current === null && ref !== null) || - (current !== null && current.ref !== ref) - ) { - // Schedule a Ref effect - workInProgress.flags |= Ref; - } -} - -function updateFunctionComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes -) { - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; - - if (innerPropTypes) { - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(Component) - ); - } - } - } - - var context; - - { - var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); - context = getMaskedContext(workInProgress, unmaskedContext); - } - - var nextChildren; - prepareToReadContext(workInProgress, renderLanes); - - { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - nextChildren = renderWithHooks( - current, - workInProgress, - Component, - nextProps, - context, - renderLanes - ); - - if (workInProgress.mode & StrictLegacyMode) { - disableLogs(); - - try { - nextChildren = renderWithHooks( - current, - workInProgress, - Component, - nextProps, - context, - renderLanes - ); - } finally { - reenableLogs(); - } - } - - setIsRendering(false); - } - - if (current !== null && !didReceiveUpdate) { - bailoutHooks(current, workInProgress, renderLanes); - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } // React DevTools reads this flag. - - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} - -function updateClassComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes -) { - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; - - if (innerPropTypes) { - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(Component) - ); - } - } - } // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. - - var hasContext; - - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); - } else { - hasContext = false; - } - - prepareToReadContext(workInProgress, renderLanes); - var instance = workInProgress.stateNode; - var shouldUpdate; - - if (instance === null) { - if (current !== null) { - // A class component without an instance only mounts if it suspended - // inside a non-concurrent tree, in an inconsistent state. We want to - // treat it like a new mount, even though an empty version of it already - // committed. Disconnect the alternate pointers. - current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - - workInProgress.flags |= Placement; - } // In the initial pass we might need to construct the instance. - - constructClassInstance(workInProgress, Component, nextProps); - mountClassInstance(workInProgress, Component, nextProps, renderLanes); - shouldUpdate = true; - } else if (current === null) { - // In a resume, we'll already have an instance we can reuse. - shouldUpdate = resumeMountClassInstance( - workInProgress, - Component, - nextProps, - renderLanes - ); - } else { - shouldUpdate = updateClassInstance( - current, - workInProgress, - Component, - nextProps, - renderLanes - ); - } - - var nextUnitOfWork = finishClassComponent( - current, - workInProgress, - Component, - shouldUpdate, - hasContext, - renderLanes - ); - - { - var inst = workInProgress.stateNode; - - if (shouldUpdate && inst.props !== nextProps) { - if (!didWarnAboutReassigningProps) { - error( - "It looks like %s is reassigning its own `this.props` while rendering. " + - "This is not supported and can lead to confusing bugs.", - getComponentNameFromFiber(workInProgress) || "a component" - ); - } - - didWarnAboutReassigningProps = true; - } - } - - return nextUnitOfWork; -} - -function finishClassComponent( - current, - workInProgress, - Component, - shouldUpdate, - hasContext, - renderLanes -) { - // Refs should update even if shouldComponentUpdate returns false - markRef(current, workInProgress); - var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; - - if (!shouldUpdate && !didCaptureError) { - // Context providers should defer to sCU for rendering - if (hasContext) { - invalidateContextProvider(workInProgress, Component, false); + if (isDevToolsPresent) { + // If we have pending work still, restore the original updaters + restorePendingUpdaters(root, rootRenderLanes); } - - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } - var instance = workInProgress.stateNode; // Rerender - - ReactCurrentOwner$1.current = workInProgress; - var nextChildren; - if ( - didCaptureError && - typeof Component.getDerivedStateFromError !== "function" + value !== null && + typeof value === "object" && + typeof value.then === "function" ) { - // If we captured an error, but getDerivedStateFromError 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; + var wakeable = value; + // A legacy mode Suspense quirk, only relevant to hook components. - { - stopProfilerTimerIfRunning(); - } - } else { - { - setIsRendering(true); - nextChildren = instance.render(); + var tag = sourceFiber.tag; - if (workInProgress.mode & StrictLegacyMode) { - disableLogs(); + if ( + (sourceFiber.mode & ConcurrentMode) === NoMode && + (tag === FunctionComponent || + tag === ForwardRef || + tag === SimpleMemoComponent) + ) { + var currentSource = sourceFiber.alternate; - try { - instance.render(); - } finally { - reenableLogs(); - } + if (currentSource) { + sourceFiber.updateQueue = currentSource.updateQueue; + sourceFiber.memoizedState = currentSource.memoizedState; + sourceFiber.lanes = currentSource.lanes; + } else { + sourceFiber.updateQueue = null; + sourceFiber.memoizedState = null; } - - setIsRendering(false); } - } // React DevTools reads this flag. - - workInProgress.flags |= PerformedWork; - - if (current !== null && didCaptureError) { - // If we're recovering from an error, reconcile without reusing any of - // the existing children. Conceptually, the normal children and the children - // that are shown on error are two different sets, so we shouldn't reuse - // normal children even if their identities match. - forceUnmountCurrentAndReconcile( - current, - workInProgress, - nextChildren, - renderLanes - ); - } else { - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - } // Memoize state using the values we just used to render. - // TODO: Restructure so we never read values from the instance. - - workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. - - if (hasContext) { - invalidateContextProvider(workInProgress, Component, true); - } - - return workInProgress.child; -} - -function pushHostRootContext(workInProgress) { - var root = workInProgress.stateNode; - - if (root.pendingContext) { - pushTopLevelContextObject( - workInProgress, - root.pendingContext, - root.pendingContext !== root.context - ); - } else if (root.context) { - // Should always be set - pushTopLevelContextObject(workInProgress, root.context, false); - } - - pushHostContainer(workInProgress, root.containerInfo); -} - -function updateHostRoot(current, workInProgress, renderLanes) { - pushHostRootContext(workInProgress); - var updateQueue = workInProgress.updateQueue; - - if (!(current !== null && updateQueue !== null)) { - throw Error( - "If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue." - ); - } - - var nextProps = workInProgress.pendingProps; - var prevState = workInProgress.memoizedState; - var prevChildren = prevState.element; - cloneUpdateQueue(current, workInProgress); - processUpdateQueue(workInProgress, nextProps, null, renderLanes); - var nextState = workInProgress.memoizedState; - var root = workInProgress.stateNode; - // being called "element". - var nextChildren = nextState.element; - - if (nextChildren === prevChildren) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } + var hasInvisibleParentBoundary = hasSuspenseContext( + suspenseStackCursor.current, + InvisibleParentSuspenseContext + ); // Schedule the nearest Suspense to re-render the timed out view. - if (root.hydrate && enterHydrationState()) { - var child = mountChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - workInProgress.child = child; - var node = child; + var _workInProgress = returnFiber; - while (node) { - // Mark each child as hydrating. This is a fast path to know whether this - // tree is part of a hydrating tree. This is used to determine if a child - // node has fully mounted yet, and for scheduling event replaying. - // Conceptually this is similar to Placement in that a new subtree is - // inserted into the React tree here. It just happens to not need DOM - // mutations because it already exists. - node.flags = (node.flags & ~Placement) | Hydrating; - node = node.sibling; - } - } else { - // Otherwise reset hydration state in case we aborted and resumed another - // root. - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - } + do { + if ( + _workInProgress.tag === SuspenseComponent && + shouldCaptureSuspense(_workInProgress, hasInvisibleParentBoundary) + ) { + // Found the nearest boundary. + // Stash the promise on the boundary fiber. If the boundary times out, we'll + // attach another listener to flip the boundary back to its normal state. + var wakeables = _workInProgress.updateQueue; - return workInProgress.child; -} + if (wakeables === null) { + var updateQueue = new Set(); + updateQueue.add(wakeable); + _workInProgress.updateQueue = updateQueue; + } else { + wakeables.add(wakeable); + } // If the boundary is in legacy mode, we should *not* + // suspend the commit. Pretend as if the suspended component rendered + // null and keep rendering. In the commit phase, we'll schedule a + // subsequent synchronous update to re-render the Suspense. + // + // Note: It doesn't matter whether the component that suspended was + // inside a concurrent mode tree. If the Suspense is outside of it, we + // should *not* suspend the commit. + // + // If the suspense boundary suspended itself suspended, we don't have to + // do this trick because nothing was partially started. We can just + // directly do a second pass over the fallback in this render and + // pretend we meant to render that directly. -function updateHostComponent(current, workInProgress, renderLanes) { - pushHostContext(workInProgress); + if ( + (_workInProgress.mode & ConcurrentMode) === NoMode && + _workInProgress !== returnFiber + ) { + _workInProgress.flags |= DidCapture; + sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. + // But we shouldn't call any lifecycle methods or callbacks. Remove + // all lifecycle effect tags. - var type = workInProgress.type; - var nextProps = workInProgress.pendingProps; - var prevProps = current !== null ? current.memoizedProps : null; - var nextChildren = nextProps.children; + sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); - if (prevProps !== null && shouldSetTextContent()) { - // If we're switching from a direct text child to a normal child, or to - // empty, we need to schedule the text content to be reset. - workInProgress.flags |= ContentReset; - } + if (sourceFiber.tag === ClassComponent) { + var _currentSourceFiber = sourceFiber.alternate; - markRef(current, workInProgress); - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} + if (_currentSourceFiber === null) { + // This is a new mount. Change the tag so it's not mistaken for a + // completed class component. For example, we should not call + // componentWillUnmount if it is deleted. + sourceFiber.tag = IncompleteClassComponent; + } else { + // When we try rendering again, we should not reuse the current fiber, + // since it's known to be in an inconsistent state. Use a force update to + // prevent a bail out. + var update = createUpdate(NoTimestamp, SyncLane); + update.tag = ForceUpdate; + enqueueUpdate(sourceFiber, update); + } + } // The source fiber did not complete. Mark it with Sync priority to + // indicate that it still has pending work. -function updateHostText(current, workInProgress) { - // immediately after. + sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); // Exit without suspending. - return null; -} + return; + } // Confirmed that the boundary is in a concurrent mode tree. Continue + // with the normal suspend path. + // + // After this we'll use a set of heuristics to determine whether this + // render pass will run to completion or restart or "suspend" the commit. + // The actual logic for this is spread out in different places. + // + // This first principle is that if we're going to suspend when we complete + // a root, then we should also restart if we get an update or ping that + // might unsuspend it, and vice versa. The only reason to suspend is + // because you think you might want to restart before committing. However, + // it doesn't make sense to restart only while in the period we're suspended. + // + // Restarting too aggressively is also not good because it starves out any + // intermediate loading state. So we use heuristics to determine when. + // Suspense Heuristics + // + // If nothing threw a Promise or all the same fallbacks are already showing, + // then don't suspend/restart. + // + // If this is an initial render of a new tree of Suspense boundaries and + // those trigger a fallback, then don't suspend/restart. We want to ensure + // that we can show the initial loading state as quickly as possible. + // + // If we hit a "Delayed" case, such as when we'd switch from content back into + // a fallback, then we should always suspend/restart. Transitions apply + // to this case. If none is defined, JND is used instead. + // + // If we're already showing a fallback and it gets "retried", allowing us to show + // another level, but there's still an inner boundary that would show a fallback, + // then we suspend/restart for 500ms since the last time we showed a fallback + // anywhere in the tree. This effectively throttles progressive loading into a + // consistent train of commits. This also gives us an opportunity to restart to + // get to the completed state slightly earlier. + // + // If there's ambiguity due to batching it's resolved in preference of: + // 1) "delayed", 2) "initial render", 3) "retry". + // + // We want to ensure that a "busy" state doesn't get force committed. We want to + // ensure that new initial loading states can commit as soon as possible. -function mountLazyComponent( - _current, - workInProgress, - elementType, - updateLanes, - renderLanes -) { - if (_current !== null) { - // A lazy component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + attachPingListener(root, wakeable, rootRenderLanes); + _workInProgress.flags |= ShouldCapture; + _workInProgress.lanes = rootRenderLanes; + return; + } // This boundary already captured during this render. Continue to the next + // boundary. - workInProgress.flags |= Placement; - } + _workInProgress = _workInProgress.return; + } while (_workInProgress !== null); // No boundary was found. Fallthrough to error mode. + // TODO: Use invariant so the message is stripped in prod? - var props = workInProgress.pendingProps; - var lazyComponent = elementType; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - var Component = init(payload); // Store the unwrapped component in the type. + value = new Error( + (getComponentNameFromFiber(sourceFiber) || "A React component") + + " suspended while rendering, but no fallback UI was specified.\n" + + "\n" + + "Add a component higher in the tree to " + + "provide a loading indicator or placeholder to display." + ); + } // We didn't find a boundary that could handle this type of exception. Start + // over and traverse parent path again, this time treating the exception + // as an error. - workInProgress.type = Component; - var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); - var resolvedProps = resolveDefaultProps(Component, props); - var child; + renderDidError(); + value = createCapturedValue(value, sourceFiber); + var workInProgress = returnFiber; - switch (resolvedTag) { - case FunctionComponent: { - { - validateFunctionComponentInDev(workInProgress, Component); - workInProgress.type = Component = resolveFunctionForHotReloading( - Component - ); - } + do { + switch (workInProgress.tag) { + case HostRoot: { + var _errorInfo = value; + workInProgress.flags |= ShouldCapture; + var lane = pickArbitraryLane(rootRenderLanes); + workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); - child = updateFunctionComponent( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } + var _update = createRootErrorUpdate(workInProgress, _errorInfo, lane); - case ClassComponent: { - { - workInProgress.type = Component = resolveClassForHotReloading( - Component - ); + enqueueCapturedUpdate(workInProgress, _update); + return; } - child = updateClassComponent( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } - - case ForwardRef: { - { - workInProgress.type = Component = resolveForwardRefForHotReloading( - Component - ); - } + case ClassComponent: + // Capture and retry + var errorInfo = value; + var ctor = workInProgress.type; + var instance = workInProgress.stateNode; - child = updateForwardRef( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } + if ( + (workInProgress.flags & DidCapture) === NoFlags && + (typeof ctor.getDerivedStateFromError === "function" || + (instance !== null && + typeof instance.componentDidCatch === "function" && + !isAlreadyFailedLegacyErrorBoundary(instance))) + ) { + workInProgress.flags |= ShouldCapture; - case MemoComponent: { - { - if (workInProgress.type !== workInProgress.elementType) { - var outerPropTypes = Component.propTypes; + var _lane = pickArbitraryLane(rootRenderLanes); - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - resolvedProps, // Resolved for outer only - "prop", - getComponentNameFromType(Component) - ); - } - } - } + workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state - child = updateMemoComponent( - null, - workInProgress, - Component, - resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too - updateLanes, - renderLanes - ); - return child; - } - } + var _update2 = createClassErrorUpdate( + workInProgress, + errorInfo, + _lane + ); - var hint = ""; + enqueueCapturedUpdate(workInProgress, _update2); + return; + } - { - if ( - Component !== null && - typeof Component === "object" && - Component.$$typeof === REACT_LAZY_TYPE - ) { - hint = " Did you wrap a component in React.lazy() more than once?"; + break; } - } // This message intentionally doesn't mention ForwardRef or MemoComponent - // because the fact that it's a separate type of work is an - // implementation detail. - { - throw Error( - "Element type is invalid. Received a promise that resolves to: " + - Component + - ". Lazy element type must resolve to a class or function." + - hint - ); - } + workInProgress = workInProgress.return; + } while (workInProgress !== null); } -function mountIncompleteClassComponent( - _current, - workInProgress, - Component, - nextProps, - renderLanes -) { - if (_current !== null) { - // An incomplete component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - - workInProgress.flags |= Placement; - } // Promote the fiber to a class and try rendering again. - - workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` - // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var didReceiveUpdate = false; +var didWarnAboutBadClass; +var didWarnAboutModulePatternComponent; +var didWarnAboutContextTypeOnFunctionComponent; +var didWarnAboutGetDerivedStateOnFunctionComponent; +var didWarnAboutFunctionRefs; +var didWarnAboutReassigningProps; +var didWarnAboutRevealOrder; +var didWarnAboutTailOptions; - var hasContext; +{ + didWarnAboutBadClass = {}; + didWarnAboutModulePatternComponent = {}; + didWarnAboutContextTypeOnFunctionComponent = {}; + didWarnAboutGetDerivedStateOnFunctionComponent = {}; + didWarnAboutFunctionRefs = {}; + didWarnAboutReassigningProps = false; + didWarnAboutRevealOrder = {}; + didWarnAboutTailOptions = {}; +} - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); +function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { + if (current === null) { + // If this is a fresh new component that hasn't been rendered yet, we + // won't update its child set by applying minimal side-effects. Instead, + // we will add them all to the child before it gets rendered. That means + // we can optimize this reconciliation pass by not tracking side-effects. + workInProgress.child = mountChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); } else { - hasContext = false; + // If the current child is the same as the work in progress, it means that + // we haven't yet started any work on these children. Therefore, we use + // the clone algorithm to create a copy of all the current children. + // If we had any progressed work already, that is invalid at this point so + // let's throw it out. + workInProgress.child = reconcileChildFibers( + workInProgress, + current.child, + nextChildren, + renderLanes + ); } +} - prepareToReadContext(workInProgress, renderLanes); - constructClassInstance(workInProgress, Component, nextProps); - mountClassInstance(workInProgress, Component, nextProps, renderLanes); - return finishClassComponent( +function forceUnmountCurrentAndReconcile( + current, + workInProgress, + nextChildren, + renderLanes +) { + // This function is fork of reconcileChildren. It's used in cases where we + // want to reconcile without matching against the existing set. This has the + // effect of all current children being unmounted; even if the type and key + // are the same, the old child is unmounted and a new child is created. + // + // To do this, we're going to go through the reconcile algorithm twice. In + // the first pass, we schedule a deletion for all the current children by + // passing null. + workInProgress.child = reconcileChildFibers( + workInProgress, + current.child, null, + renderLanes + ); // In the second pass, we mount the new children. The trick here is that we + // pass null in place of where we usually pass the current child set. This has + // the effect of remounting all children regardless of whether their + // identities match. + + workInProgress.child = reconcileChildFibers( workInProgress, - Component, - true, - hasContext, + null, + nextChildren, renderLanes ); } -function mountIndeterminateComponent( - _current, +function updateForwardRef( + current, workInProgress, Component, + nextProps, renderLanes ) { - if (_current !== null) { - // An indeterminate component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + // TODO: current can be non-null here even if the component + // hasn't yet mounted. This happens after the first render suspends. + // We'll need to figure out if this is fine or can cause issues. + { + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; - workInProgress.flags |= Placement; + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) + ); + } + } } - var props = workInProgress.pendingProps; - var context; - - { - var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); - context = getMaskedContext(workInProgress, unmaskedContext); - } + var render = Component.render; + var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent + var nextChildren; prepareToReadContext(workInProgress, renderLanes); - var value; { - if ( - Component.prototype && - typeof Component.prototype.render === "function" - ) { - var componentName = getComponentNameFromType(Component) || "Unknown"; + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + nextChildren = renderWithHooks( + current, + workInProgress, + render, + nextProps, + ref, + renderLanes + ); - if (!didWarnAboutBadClass[componentName]) { - error( - "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + - "This is likely to cause errors. Change %s to extend React.Component instead.", - componentName, - componentName - ); + if (workInProgress.mode & StrictLegacyMode) { + disableLogs(); - didWarnAboutBadClass[componentName] = true; + try { + nextChildren = renderWithHooks( + current, + workInProgress, + render, + nextProps, + ref, + renderLanes + ); + } finally { + reenableLogs(); } } - if (workInProgress.mode & StrictLegacyMode) { - ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); - } - - setIsRendering(true); - ReactCurrentOwner$1.current = workInProgress; - value = renderWithHooks( - null, - workInProgress, - Component, - props, - context, - renderLanes - ); setIsRendering(false); + } + + if (current !== null && !didReceiveUpdate) { + bailoutHooks(current, workInProgress, renderLanes); + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } // React DevTools reads this flag. workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} + +function updateMemoComponent( + current, + workInProgress, + Component, + nextProps, + updateLanes, + renderLanes +) { + if (current === null) { + var type = Component.type; - { - // Support for module components is deprecated and is removed behind a flag. - // Whether or not it would crash later, we want to show a good message in DEV first. if ( - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined + isSimpleFunctionComponent(type) && + Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. + Component.defaultProps === undefined ) { - var _componentName = getComponentNameFromType(Component) || "Unknown"; + var resolvedType = type; - if (!didWarnAboutModulePatternComponent[_componentName]) { - error( - "The <%s /> component appears to be a function component that returns a class instance. " + - "Change %s to a class that extends React.Component instead. " + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - "cannot be called with `new` by React.", - _componentName, - _componentName, - _componentName - ); + { + resolvedType = resolveFunctionForHotReloading(type); + } // If this is a plain function component without default props, + // and with only the default shallow comparison, we upgrade it + // to a SimpleMemoComponent to allow fast path updates. + + workInProgress.tag = SimpleMemoComponent; + workInProgress.type = resolvedType; - didWarnAboutModulePatternComponent[_componentName] = true; + { + validateFunctionComponentInDev(workInProgress, type); } + + return updateSimpleMemoComponent( + current, + workInProgress, + resolvedType, + nextProps, + updateLanes, + renderLanes + ); } - } - if ( - // Run these checks in production only if the flag is off. - // Eventually we'll delete this branch altogether. - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined - ) { { - var _componentName2 = getComponentNameFromType(Component) || "Unknown"; + var innerPropTypes = type.propTypes; - if (!didWarnAboutModulePatternComponent[_componentName2]) { - error( - "The <%s /> component appears to be a function component that returns a class instance. " + - "Change %s to a class that extends React.Component instead. " + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - "cannot be called with `new` by React.", - _componentName2, - _componentName2, - _componentName2 + if (innerPropTypes) { + // Inner memo component props aren't currently validated in createElement. + // We could move it there, but we'd still need this for lazy code path. + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(type) ); - - didWarnAboutModulePatternComponent[_componentName2] = true; } - } // Proceed under the assumption that this is a class instance - - workInProgress.tag = ClassComponent; // Throw out any hooks that were used. - - workInProgress.memoizedState = null; - workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. - - var hasContext = false; - - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); - } else { - hasContext = false; } - workInProgress.memoizedState = - value.state !== null && value.state !== undefined ? value.state : null; - initializeUpdateQueue(workInProgress); - adoptClassInstance(workInProgress, value); - mountClassInstance(workInProgress, Component, props, renderLanes); - return finishClassComponent( + var child = createFiberFromTypeAndProps( + Component.type, null, + nextProps, workInProgress, - Component, - true, - hasContext, + workInProgress.mode, renderLanes ); - } else { - // Proceed under the assumption that this is a function component - workInProgress.tag = FunctionComponent; + child.ref = workInProgress.ref; + child.return = workInProgress; + workInProgress.child = child; + return child; + } - { - if (workInProgress.mode & StrictLegacyMode) { - disableLogs(); + { + var _type = Component.type; + var _innerPropTypes = _type.propTypes; - try { - value = renderWithHooks( - null, - workInProgress, - Component, - props, - context, - renderLanes - ); - } finally { - reenableLogs(); - } - } + if (_innerPropTypes) { + // Inner memo component props aren't currently validated in createElement. + // We could move it there, but we'd still need this for lazy code path. + checkPropTypes( + _innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(_type) + ); } + } - reconcileChildren(null, workInProgress, value, renderLanes); + var currentChild = current.child; // This is always exactly one child - { - validateFunctionComponentInDev(workInProgress, Component); - } + if (!includesSomeLane(updateLanes, renderLanes)) { + // This will be the props with resolved defaultProps, + // unlike current.memoizedProps which will be the unresolved ones. + var prevProps = currentChild.memoizedProps; // Default to shallow comparison - return workInProgress.child; - } -} + var compare = Component.compare; + compare = compare !== null ? compare : shallowEqual; -function validateFunctionComponentInDev(workInProgress, Component) { - { - if (Component) { - if (Component.childContextTypes) { - error( - "%s(...): childContextTypes cannot be defined on a function component.", - Component.displayName || Component.name || "Component" - ); - } + if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } + } // React DevTools reads this flag. - if (workInProgress.ref !== null) { - var info = ""; - var ownerName = getCurrentFiberOwnerNameInDevOrNull(); + workInProgress.flags |= PerformedWork; + var newChild = createWorkInProgress(currentChild, nextProps); + newChild.ref = workInProgress.ref; + newChild.return = workInProgress; + workInProgress.child = newChild; + return newChild; +} - if (ownerName) { - info += "\n\nCheck the render method of `" + ownerName + "`."; - } +function updateSimpleMemoComponent( + current, + workInProgress, + Component, + nextProps, + updateLanes, + renderLanes +) { + // TODO: current can be non-null here even if the component + // hasn't yet mounted. This happens when the inner render suspends. + // We'll need to figure out if this is fine or can cause issues. + { + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var outerMemoType = workInProgress.elementType; - var warningKey = ownerName || workInProgress._debugID || ""; - var debugSource = workInProgress._debugSource; + if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { + // We warn when you define propTypes on lazy() + // so let's just skip over it to find memo() outer wrapper. + // Inner props for memo are validated later. + var lazyComponent = outerMemoType; + var payload = lazyComponent._payload; + var init = lazyComponent._init; - if (debugSource) { - warningKey = debugSource.fileName + ":" + debugSource.lineNumber; - } + try { + outerMemoType = init(payload); + } catch (x) { + outerMemoType = null; + } // Inner propTypes will be validated in the function component path. - if (!didWarnAboutFunctionRefs[warningKey]) { - didWarnAboutFunctionRefs[warningKey] = true; + var outerPropTypes = outerMemoType && outerMemoType.propTypes; - error( - "Function components cannot be given refs. " + - "Attempts to access this ref will fail. " + - "Did you mean to use React.forwardRef()?%s", - info - ); + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + nextProps, // Resolved (SimpleMemoComponent has no defaultProps) + "prop", + getComponentNameFromType(outerMemoType) + ); + } } } + } - if (typeof Component.getDerivedStateFromProps === "function") { - var _componentName3 = getComponentNameFromType(Component) || "Unknown"; - - if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { - error( - "%s: Function components do not support getDerivedStateFromProps.", - _componentName3 - ); - - didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; - } - } + if (current !== null) { + var prevProps = current.memoizedProps; if ( - typeof Component.contextType === "object" && - Component.contextType !== null + shallowEqual(prevProps, nextProps) && + current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload. + workInProgress.type === current.type ) { - var _componentName4 = getComponentNameFromType(Component) || "Unknown"; + didReceiveUpdate = false; - if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { - error( - "%s: Function components do not support contextType.", - _componentName4 + if (!includesSomeLane(renderLanes, updateLanes)) { + // The pending lanes were cleared at the beginning of beginWork. We're + // about to bail out, but there might be other lanes that weren't + // included in the current render. Usually, the priority level of the + // remaining updates is accumulated during the evaluation of the + // component (i.e. when processing the update queue). But since since + // we're bailing out early *without* evaluating the component, we need + // to account for it here, too. Reset to the value of the current fiber. + // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, + // because a MemoComponent fiber does not have hooks or an update queue; + // rather, it wraps around an inner component, which may or may not + // contains hooks. + // TODO: Move the reset at in beginWork out of the common path so that + // this is no longer necessary. + workInProgress.lanes = current.lanes; + return bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes ); + } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { + // This is a special case that only exists for legacy mode. + // See https://github.com/facebook/react/pull/19216. + didReceiveUpdate = true; + } + } + } - didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; - } - } - } -} - -var SUSPENDED_MARKER = { - dehydrated: null, - retryLane: NoLane -}; - -function mountSuspenseOffscreenState(renderLanes) { - return { - baseLanes: renderLanes, - cachePool: getSuspendedCachePool() - }; + return updateFunctionComponent( + current, + workInProgress, + Component, + nextProps, + renderLanes + ); } -function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { - var cachePool = null; +function updateOffscreenComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; + var nextChildren = nextProps.children; + var prevState = current !== null ? current.memoizedState : null; // If this is not null, this is a cache pool that was carried over from the + // previous render. We will push this to the cache pool context so that we can + // resume in-flight requests. - return { - baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), - cachePool: cachePool - }; -} // TODO: Probably should inline this back + var spawnedCachePool = null; -function shouldRemainOnFallback( - suspenseContext, - current, - workInProgress, - renderLanes -) { - // If we're already showing a fallback, there are cases where we need to - // remain on that fallback regardless of whether the content has resolved. - // For example, SuspenseList coordinates when nested content appears. - if (current !== null) { - var suspenseState = current.memoizedState; + if ( + nextProps.mode === "hidden" || + nextProps.mode === "unstable-defer-without-hiding" + ) { + // Rendering a hidden tree. + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + // In legacy sync mode, don't defer the subtree. Render it now. + var nextState = { + baseLanes: NoLanes, + cachePool: null + }; + workInProgress.memoizedState = nextState; + pushRenderLanes(workInProgress, renderLanes); + } else if (!includesSomeLane(renderLanes, OffscreenLane)) { + // We're hidden, and we're not rendering at Offscreen. We will bail out + // and resume this tree later. + var nextBaseLanes; - if (suspenseState === null) { - // Currently showing content. Don't hide it, even if ForceSuspenseFallack - // is true. More precise name might be "ForceRemainSuspenseFallback". - // Note: This is a factoring smell. Can't remain on a fallback if there's - // no fallback to remain on. - return false; - } - } // Not currently showing content. Consult the Suspense context. + if (prevState !== null) { + var prevBaseLanes = prevState.baseLanes; + nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); + } else { + nextBaseLanes = renderLanes; + } // Schedule this fiber to re-render at offscreen priority. Then bailout. - return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); -} + workInProgress.lanes = workInProgress.childLanes = laneToLanes( + OffscreenLane + ); + var _nextState = { + baseLanes: nextBaseLanes, + cachePool: spawnedCachePool + }; + workInProgress.memoizedState = _nextState; + workInProgress.updateQueue = null; // We're about to bail out, but we need to push this to the stack anyway + // to avoid a push/pop misalignment. -function getRemainingWorkInPrimaryTree(current, renderLanes) { - // TODO: Should not remove render lanes that were pinged during this render - return removeLanes(current.childLanes, renderLanes); -} + pushRenderLanes(workInProgress, nextBaseLanes); -function updateSuspenseComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. + return null; + } else { + var _nextState2 = { + baseLanes: NoLanes, + cachePool: null + }; + workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. - { - if (shouldSuspend(workInProgress)) { - workInProgress.flags |= DidCapture; + var subtreeRenderLanes = + prevState !== null ? prevState.baseLanes : renderLanes; + pushRenderLanes(workInProgress, subtreeRenderLanes); } - } + } else { + // Rendering a visible tree. + var _subtreeRenderLanes; - var suspenseContext = suspenseStackCursor.current; - var showFallback = false; - var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; + if (prevState !== null) { + // We're going from hidden -> visible. + _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); - if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { - // Something in this boundary's subtree already suspended. Switch to - // rendering the fallback children. - showFallback = true; - workInProgress.flags &= ~DidCapture; - } else { - // Attempting the main content - if (current === null || current.memoizedState !== null) { - // This is a new mount or this boundary is already showing a fallback state. - // Mark this subtree context as having at least one invisible parent that could - // handle the fallback state. - // Boundaries without fallbacks or should be avoided are not considered since - // they cannot handle preferred fallback states. - if ( - nextProps.fallback !== undefined && - nextProps.unstable_avoidThisFallback !== true - ) { - suspenseContext = addSubtreeSuspenseContext( - suspenseContext, - InvisibleParentSuspenseContext - ); - } + workInProgress.memoizedState = null; + } else { + // We weren't previously hidden, and we still aren't, so there's nothing + // special to do. Need to push to the stack regardless, though, to avoid + // a push/pop misalignment. + _subtreeRenderLanes = renderLanes; } + + pushRenderLanes(workInProgress, _subtreeRenderLanes); } - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense - // boundary's children. This involves some custom reconcilation logic. Two - // main reasons this is so complicated. - // - // First, Legacy Mode has different semantics for backwards compatibility. The - // primary tree will commit in an inconsistent state, so when we do the - // second pass to render the fallback, we do some exceedingly, uh, clever - // hacks to make that not totally break. Like transferring effects and - // deletions from hidden tree. In Concurrent Mode, it's much simpler, - // because we bailout on the primary tree completely and leave it in its old - // state, no effects. Same as what we do for Offscreen (except that - // Offscreen doesn't have the first render pass). - // - // Second is hydration. During hydration, the Suspense fiber has a slightly - // different layout, where the child points to a dehydrated fragment, which - // contains the DOM rendered by the server. - // - // Third, even if you set all that aside, Suspense is like error boundaries in - // that we first we try to render one tree, and if that fails, we render again - // and switch to a different tree. Like a try/catch block. So we have to track - // which branch we're currently rendering. Ideally we would model this using - // a stack. + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} // Note: These happen to have identical begin phases, for now. We shouldn't hold +// ourselves to this constraint, though. If the behavior diverges, we should +// fork the function. - if (current === null) { - // Initial mount - // If we're currently hydrating, try to hydrate this boundary. - // But only if this has a fallback. - if (nextProps.fallback !== undefined); +var updateLegacyHiddenComponent = updateOffscreenComponent; - var nextPrimaryChildren = nextProps.children; - var nextFallbackChildren = nextProps.fallback; +function updateFragment(current, workInProgress, renderLanes) { + var nextChildren = workInProgress.pendingProps; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} - if (showFallback) { - var fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ); - var primaryChildFragment = workInProgress.child; - primaryChildFragment.memoizedState = mountSuspenseOffscreenState( - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; - return fallbackFragment; - } else if (typeof nextProps.unstable_expectedLoadTime === "number") { - // This is a CPU-bound tree. Skip this tree and show a placeholder to - // unblock the surrounding content. Then immediately retry after the - // initial commit. - var _fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ); +function updateMode(current, workInProgress, renderLanes) { + var nextChildren = workInProgress.pendingProps.children; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} - var _primaryChildFragment = workInProgress.child; - _primaryChildFragment.memoizedState = mountSuspenseOffscreenState( - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; // Since nothing actually suspended, there will nothing to ping this to - // get it started back up to attempt the next item. While in terms of - // priority this work has the same priority as this current render, it's - // not part of the same transition once the transition has committed. If - // it's sync, we still want to yield so that it can be painted. - // Conceptually, this is really the same as pinging. We can use any - // RetryLane even if it's the one currently rendering since we're leaving - // it behind on this node. +function updateProfiler(current, workInProgress, renderLanes) { + { + workInProgress.flags |= Update; - workInProgress.lanes = SomeRetryLane; - return _fallbackFragment; - } else { - return mountSuspensePrimaryChildren( - workInProgress, - nextPrimaryChildren, - renderLanes - ); + { + // Reset effect durations for the next eventual effect phase. + // These are reset during render to allow the DevTools commit hook a chance to read them, + var stateNode = workInProgress.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; } - } else { - // This is an update. - // If the current fiber has a SuspenseState, that means it's already showing - // a fallback. - var prevState = current.memoizedState; + } - if (prevState !== null) { - if (showFallback) { - var _nextFallbackChildren2 = nextProps.fallback; - var _nextPrimaryChildren2 = nextProps.children; + var nextProps = workInProgress.pendingProps; + var nextChildren = nextProps.children; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} + +function markRef(current, workInProgress) { + var ref = workInProgress.ref; - var _fallbackChildFragment = updateSuspenseFallbackChildren( - current, - workInProgress, - _nextPrimaryChildren2, - _nextFallbackChildren2, - renderLanes - ); + if ( + (current === null && ref !== null) || + (current !== null && current.ref !== ref) + ) { + // Schedule a Ref effect + workInProgress.flags |= Ref; + } +} - var _primaryChildFragment3 = workInProgress.child; - var prevOffscreenState = current.child.memoizedState; - _primaryChildFragment3.memoizedState = - prevOffscreenState === null - ? mountSuspenseOffscreenState(renderLanes) - : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); - _primaryChildFragment3.childLanes = getRemainingWorkInPrimaryTree( - current, - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; - return _fallbackChildFragment; - } else { - var _nextPrimaryChildren3 = nextProps.children; +function updateFunctionComponent( + current, + workInProgress, + Component, + nextProps, + renderLanes +) { + { + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; - var _primaryChildFragment4 = updateSuspensePrimaryChildren( - current, - workInProgress, - _nextPrimaryChildren3, - renderLanes + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) ); - - workInProgress.memoizedState = null; - return _primaryChildFragment4; } - } else { - // The current tree is not already showing a fallback. - if (showFallback) { - // Timed out. - var _nextFallbackChildren3 = nextProps.fallback; - var _nextPrimaryChildren4 = nextProps.children; + } + } - var _fallbackChildFragment2 = updateSuspenseFallbackChildren( - current, - workInProgress, - _nextPrimaryChildren4, - _nextFallbackChildren3, - renderLanes - ); + var context; - var _primaryChildFragment5 = workInProgress.child; - var _prevOffscreenState = current.child.memoizedState; - _primaryChildFragment5.memoizedState = - _prevOffscreenState === null - ? mountSuspenseOffscreenState(renderLanes) - : updateSuspenseOffscreenState(_prevOffscreenState, renderLanes); - _primaryChildFragment5.childLanes = getRemainingWorkInPrimaryTree( - current, - renderLanes - ); // Skip the primary children, and continue working on the - // fallback children. + { + var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); + context = getMaskedContext(workInProgress, unmaskedContext); + } - workInProgress.memoizedState = SUSPENDED_MARKER; - return _fallbackChildFragment2; - } else { - // Still haven't timed out. Continue rendering the children, like we - // normally do. - var _nextPrimaryChildren5 = nextProps.children; + var nextChildren; + prepareToReadContext(workInProgress, renderLanes); - var _primaryChildFragment6 = updateSuspensePrimaryChildren( + { + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + nextChildren = renderWithHooks( + current, + workInProgress, + Component, + nextProps, + context, + renderLanes + ); + + if (workInProgress.mode & StrictLegacyMode) { + disableLogs(); + + try { + nextChildren = renderWithHooks( current, workInProgress, - _nextPrimaryChildren5, + Component, + nextProps, + context, renderLanes ); - - workInProgress.memoizedState = null; - return _primaryChildFragment6; + } finally { + reenableLogs(); } } + + setIsRendering(false); } -} -function mountSuspensePrimaryChildren( - workInProgress, - primaryChildren, - renderLanes -) { - var mode = workInProgress.mode; - var primaryChildProps = { - mode: "visible", - children: primaryChildren - }; - var primaryChildFragment = createFiberFromOffscreen( - primaryChildProps, - mode, - renderLanes, - null - ); - primaryChildFragment.return = workInProgress; - workInProgress.child = primaryChildFragment; - return primaryChildFragment; + if (current !== null && !didReceiveUpdate) { + bailoutHooks(current, workInProgress, renderLanes); + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } // React DevTools reads this flag. + + workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function mountSuspenseFallbackChildren( +function updateClassComponent( + current, workInProgress, - primaryChildren, - fallbackChildren, + Component, + nextProps, renderLanes ) { - var mode = workInProgress.mode; - var progressedPrimaryFragment = workInProgress.child; - var primaryChildProps = { - mode: "hidden", - children: primaryChildren - }; - var primaryChildFragment; - var fallbackChildFragment; + { + // This is used by DevTools to force a boundary to error. + switch (shouldError(workInProgress)) { + case false: { + var _instance = workInProgress.stateNode; + var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack. + // Is there a better way to do this? + + var tempInstance = new ctor( + workInProgress.memoizedProps, + _instance.context + ); + var state = tempInstance.state; - if ( - (mode & ConcurrentMode) === NoMode && - progressedPrimaryFragment !== null - ) { - // In legacy mode, we commit the primary tree as if it successfully - // completed, even though it's in an inconsistent state. - primaryChildFragment = progressedPrimaryFragment; - primaryChildFragment.childLanes = NoLanes; - primaryChildFragment.pendingProps = primaryChildProps; + _instance.updater.enqueueSetState(_instance, state, null); - if (workInProgress.mode & ProfileMode) { - // Reset the durations from the first pass so they aren't included in the - // final amounts. This seems counterintuitive, since we're intentionally - // not measuring part of the render phase, but this makes it match what we - // do in Concurrent Mode. - primaryChildFragment.actualDuration = 0; - primaryChildFragment.actualStartTime = -1; - primaryChildFragment.selfBaseDuration = 0; - primaryChildFragment.treeBaseDuration = 0; + break; + } + + case true: { + workInProgress.flags |= DidCapture; + workInProgress.flags |= ShouldCapture; + var error$1 = new Error("Simulated error coming from DevTools"); + var lane = pickArbitraryLane(renderLanes); + workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state + + var update = createClassErrorUpdate( + workInProgress, + createCapturedValue(error$1, workInProgress), + lane + ); + enqueueCapturedUpdate(workInProgress, update); + break; + } } - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null - ); + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; + + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) + ); + } + } + } // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. + + var hasContext; + + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); } else { - primaryChildFragment = createFiberFromOffscreen( - primaryChildProps, - mode, - NoLanes, - null + hasContext = false; + } + + prepareToReadContext(workInProgress, renderLanes); + var instance = workInProgress.stateNode; + var shouldUpdate; + + if (instance === null) { + if (current !== null) { + // A class component without an instance only mounts if it suspended + // inside a non-concurrent tree, in an inconsistent state. We want to + // treat it like a new mount, even though an empty version of it already + // committed. Disconnect the alternate pointers. + current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + + workInProgress.flags |= Placement; + } // In the initial pass we might need to construct the instance. + + constructClassInstance(workInProgress, Component, nextProps); + mountClassInstance(workInProgress, Component, nextProps, renderLanes); + shouldUpdate = true; + } else if (current === null) { + // In a resume, we'll already have an instance we can reuse. + shouldUpdate = resumeMountClassInstance( + workInProgress, + Component, + nextProps, + renderLanes ); - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null + } else { + shouldUpdate = updateClassInstance( + current, + workInProgress, + Component, + nextProps, + renderLanes ); } - primaryChildFragment.return = workInProgress; - fallbackChildFragment.return = workInProgress; - primaryChildFragment.sibling = fallbackChildFragment; - workInProgress.child = primaryChildFragment; - return fallbackChildFragment; -} + var nextUnitOfWork = finishClassComponent( + current, + workInProgress, + Component, + shouldUpdate, + hasContext, + renderLanes + ); -function createWorkInProgressOffscreenFiber(current, offscreenProps) { - // The props argument to `createWorkInProgress` is `any` typed, so we use this - // wrapper function to constrain it. - return createWorkInProgress(current, offscreenProps); + { + var inst = workInProgress.stateNode; + + if (shouldUpdate && inst.props !== nextProps) { + if (!didWarnAboutReassigningProps) { + error( + "It looks like %s is reassigning its own `this.props` while rendering. " + + "This is not supported and can lead to confusing bugs.", + getComponentNameFromFiber(workInProgress) || "a component" + ); + } + + didWarnAboutReassigningProps = true; + } + } + + return nextUnitOfWork; } -function updateSuspensePrimaryChildren( +function finishClassComponent( current, workInProgress, - primaryChildren, + Component, + shouldUpdate, + hasContext, renderLanes ) { - var currentPrimaryChildFragment = current.child; - var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; - var primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, - { - mode: "visible", - children: primaryChildren + // Refs should update even if shouldComponentUpdate returns false + markRef(current, workInProgress); + var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; + + if (!shouldUpdate && !didCaptureError) { + // Context providers should defer to sCU for rendering + if (hasContext) { + invalidateContextProvider(workInProgress, Component, false); } - ); - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - primaryChildFragment.lanes = renderLanes; + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); } - primaryChildFragment.return = workInProgress; - primaryChildFragment.sibling = null; + var instance = workInProgress.stateNode; // Rerender - if (currentFallbackChildFragment !== null) { - // Delete the fallback child fragment - var deletions = workInProgress.deletions; + ReactCurrentOwner$1.current = workInProgress; + var nextChildren; - if (deletions === null) { - workInProgress.deletions = [currentFallbackChildFragment]; - workInProgress.flags |= ChildDeletion; - } else { - deletions.push(currentFallbackChildFragment); + if ( + didCaptureError && + typeof Component.getDerivedStateFromError !== "function" + ) { + // If we captured an error, but getDerivedStateFromError 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; + + { + stopProfilerTimerIfRunning(); + } + } else { + { + setIsRendering(true); + nextChildren = instance.render(); + + if (workInProgress.mode & StrictLegacyMode) { + disableLogs(); + + try { + instance.render(); + } finally { + reenableLogs(); + } + } + + setIsRendering(false); } + } // React DevTools reads this flag. + + workInProgress.flags |= PerformedWork; + + if (current !== null && didCaptureError) { + // If we're recovering from an error, reconcile without reusing any of + // the existing children. Conceptually, the normal children and the children + // that are shown on error are two different sets, so we shouldn't reuse + // normal children even if their identities match. + forceUnmountCurrentAndReconcile( + current, + workInProgress, + nextChildren, + renderLanes + ); + } else { + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } // Memoize state using the values we just used to render. + // TODO: Restructure so we never read values from the instance. + + workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. + + if (hasContext) { + invalidateContextProvider(workInProgress, Component, true); } - workInProgress.child = primaryChildFragment; - return primaryChildFragment; + return workInProgress.child; } -function updateSuspenseFallbackChildren( - current, - workInProgress, - primaryChildren, - fallbackChildren, - renderLanes -) { - var mode = workInProgress.mode; - var currentPrimaryChildFragment = current.child; - var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; - var primaryChildProps = { - mode: "hidden", - children: primaryChildren - }; - var primaryChildFragment; +function pushHostRootContext(workInProgress) { + var root = workInProgress.stateNode; - if ( - // In legacy mode, we commit the primary tree as if it successfully - // completed, even though it's in an inconsistent state. - (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was - // already cloned. In legacy mode, the only case where this isn't true is - // when DevTools forces us to display a fallback; we skip the first render - // pass entirely and go straight to rendering the fallback. (In Concurrent - // Mode, SuspenseList can also trigger this scenario, but this is a legacy- - // only codepath.) - workInProgress.child !== currentPrimaryChildFragment - ) { - var progressedPrimaryFragment = workInProgress.child; - primaryChildFragment = progressedPrimaryFragment; - primaryChildFragment.childLanes = NoLanes; - primaryChildFragment.pendingProps = primaryChildProps; + if (root.pendingContext) { + pushTopLevelContextObject( + workInProgress, + root.pendingContext, + root.pendingContext !== root.context + ); + } else if (root.context) { + // Should always be set + pushTopLevelContextObject(workInProgress, root.context, false); + } - if (workInProgress.mode & ProfileMode) { - // Reset the durations from the first pass so they aren't included in the - // final amounts. This seems counterintuitive, since we're intentionally - // not measuring part of the render phase, but this makes it match what we - // do in Concurrent Mode. - primaryChildFragment.actualDuration = 0; - primaryChildFragment.actualStartTime = -1; - primaryChildFragment.selfBaseDuration = - currentPrimaryChildFragment.selfBaseDuration; - primaryChildFragment.treeBaseDuration = - currentPrimaryChildFragment.treeBaseDuration; - } // The fallback fiber was added as a deletion during the first pass. - // However, since we're going to remain on the fallback, we no longer want - // to delete it. + pushHostContainer(workInProgress, root.containerInfo); +} - workInProgress.deletions = null; - } else { - primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, - primaryChildProps - ); // Since we're reusing a current tree, we need to reuse the flags, too. - // (We don't do this in legacy mode, because in legacy mode we don't re-use - // the current tree; see previous branch.) +function updateHostRoot(current, workInProgress, renderLanes) { + pushHostRootContext(workInProgress); + var updateQueue = workInProgress.updateQueue; - primaryChildFragment.subtreeFlags = - currentPrimaryChildFragment.subtreeFlags & StaticMask; + if (!(current !== null && updateQueue !== null)) { + throw Error( + "If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue." + ); + } + + var nextProps = workInProgress.pendingProps; + var prevState = workInProgress.memoizedState; + var prevChildren = prevState.element; + cloneUpdateQueue(current, workInProgress); + processUpdateQueue(workInProgress, nextProps, null, renderLanes); + var nextState = workInProgress.memoizedState; + var root = workInProgress.stateNode; + // being called "element". + + var nextChildren = nextState.element; + + if (nextChildren === prevChildren) { + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } + + if (root.hydrate && enterHydrationState()) { + var child = mountChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); + workInProgress.child = child; + var node = child; + + while (node) { + // Mark each child as hydrating. This is a fast path to know whether this + // tree is part of a hydrating tree. This is used to determine if a child + // node has fully mounted yet, and for scheduling event replaying. + // Conceptually this is similar to Placement in that a new subtree is + // inserted into the React tree here. It just happens to not need DOM + // mutations because it already exists. + node.flags = (node.flags & ~Placement) | Hydrating; + node = node.sibling; + } + } else { + // Otherwise reset hydration state in case we aborted and resumed another + // root. + reconcileChildren(current, workInProgress, nextChildren, renderLanes); } - var fallbackChildFragment; + return workInProgress.child; +} - if (currentFallbackChildFragment !== null) { - fallbackChildFragment = createWorkInProgress( - currentFallbackChildFragment, - fallbackChildren - ); - } else { - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null - ); // Needs a placement effect because the parent (the Suspense boundary) already - // mounted but this is a new fiber. +function updateHostComponent(current, workInProgress, renderLanes) { + pushHostContext(workInProgress); - fallbackChildFragment.flags |= Placement; + var type = workInProgress.type; + var nextProps = workInProgress.pendingProps; + var prevProps = current !== null ? current.memoizedProps : null; + var nextChildren = nextProps.children; + + if (prevProps !== null && shouldSetTextContent()) { + // If we're switching from a direct text child to a normal child, or to + // empty, we need to schedule the text content to be reset. + workInProgress.flags |= ContentReset; } - fallbackChildFragment.return = workInProgress; - primaryChildFragment.return = workInProgress; - primaryChildFragment.sibling = fallbackChildFragment; - workInProgress.child = primaryChildFragment; - return fallbackChildFragment; + markRef(current, workInProgress); + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function scheduleWorkOnFiber(fiber, renderLanes) { - fiber.lanes = mergeLanes(fiber.lanes, renderLanes); - var alternate = fiber.alternate; - - if (alternate !== null) { - alternate.lanes = mergeLanes(alternate.lanes, renderLanes); - } +function updateHostText(current, workInProgress) { + // immediately after. - scheduleWorkOnParentPath(fiber.return, renderLanes); + return null; } -function propagateSuspenseContextChange( +function mountLazyComponent( + _current, workInProgress, - firstChild, + elementType, + updateLanes, renderLanes ) { - // Mark any Suspense boundaries with fallbacks as having work to do. - // If they were previously forced into fallbacks, they may now be able - // to unblock. - var node = firstChild; + if (_current !== null) { + // A lazy component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - while (node !== null) { - if (node.tag === SuspenseComponent) { - var state = node.memoizedState; + workInProgress.flags |= Placement; + } - if (state !== null) { - scheduleWorkOnFiber(node, renderLanes); + var props = workInProgress.pendingProps; + var lazyComponent = elementType; + var payload = lazyComponent._payload; + var init = lazyComponent._init; + var Component = init(payload); // Store the unwrapped component in the type. + + workInProgress.type = Component; + var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); + var resolvedProps = resolveDefaultProps(Component, props); + var child; + + switch (resolvedTag) { + case FunctionComponent: { + { + validateFunctionComponentInDev(workInProgress, Component); + workInProgress.type = Component = resolveFunctionForHotReloading( + Component + ); } - } else if (node.tag === SuspenseListComponent) { - // If the tail is hidden there might not be an Suspense boundaries - // to schedule work on. In this case we have to schedule it on the - // list itself. - // We don't have to traverse to the children of the list since - // the list will propagate the change when it rerenders. - scheduleWorkOnFiber(node, renderLanes); - } else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } - if (node === workInProgress) { - return; + child = updateFunctionComponent( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; } - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; + case ClassComponent: { + { + workInProgress.type = Component = resolveClassForHotReloading( + Component + ); } - node = node.return; + child = updateClassComponent( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; } - node.sibling.return = node.return; - node = node.sibling; - } -} + case ForwardRef: { + { + workInProgress.type = Component = resolveForwardRefForHotReloading( + Component + ); + } -function findLastContentRow(firstChild) { - // This is going to find the last row among these children that is already - // showing content on the screen, as opposed to being in fallback state or - // new. If a row has multiple Suspense boundaries, any of them being in the - // fallback state, counts as the whole row being in a fallback state. - // Note that the "rows" will be workInProgress, but any nested children - // will still be current since we haven't rendered them yet. The mounted - // order may not be the same as the new order. We use the new order. - var row = firstChild; - var lastContentRow = null; + child = updateForwardRef( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; + } - while (row !== null) { - var currentRow = row.alternate; // New rows can't be content rows. + case MemoComponent: { + { + if (workInProgress.type !== workInProgress.elementType) { + var outerPropTypes = Component.propTypes; - if (currentRow !== null && findFirstSuspended(currentRow) === null) { - lastContentRow = row; - } + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + resolvedProps, // Resolved for outer only + "prop", + getComponentNameFromType(Component) + ); + } + } + } - row = row.sibling; + child = updateMemoComponent( + null, + workInProgress, + Component, + resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too + updateLanes, + renderLanes + ); + return child; + } } - return lastContentRow; -} + var hint = ""; -function validateRevealOrder(revealOrder) { { if ( - revealOrder !== undefined && - revealOrder !== "forwards" && - revealOrder !== "backwards" && - revealOrder !== "together" && - !didWarnAboutRevealOrder[revealOrder] + Component !== null && + typeof Component === "object" && + Component.$$typeof === REACT_LAZY_TYPE ) { - didWarnAboutRevealOrder[revealOrder] = true; + hint = " Did you wrap a component in React.lazy() more than once?"; + } + } // This message intentionally doesn't mention ForwardRef or MemoComponent + // because the fact that it's a separate type of work is an + // implementation detail. - if (typeof revealOrder === "string") { - switch (revealOrder.toLowerCase()) { - case "together": - case "forwards": - case "backwards": { - error( - '"%s" is not a valid value for revealOrder on . ' + - 'Use lowercase "%s" instead.', - revealOrder, - revealOrder.toLowerCase() - ); + { + throw Error( + "Element type is invalid. Received a promise that resolves to: " + + Component + + ". Lazy element type must resolve to a class or function." + + hint + ); + } +} + +function mountIncompleteClassComponent( + _current, + workInProgress, + Component, + nextProps, + renderLanes +) { + if (_current !== null) { + // An incomplete component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + + workInProgress.flags |= Placement; + } // Promote the fiber to a class and try rendering again. + + workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` + // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. + + var hasContext; + + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; + } - break; - } + prepareToReadContext(workInProgress, renderLanes); + constructClassInstance(workInProgress, Component, nextProps); + mountClassInstance(workInProgress, Component, nextProps, renderLanes); + return finishClassComponent( + null, + workInProgress, + Component, + true, + hasContext, + renderLanes + ); +} - case "forward": - case "backward": { - error( - '"%s" is not a valid value for revealOrder on . ' + - 'React uses the -s suffix in the spelling. Use "%ss" instead.', - revealOrder, - revealOrder.toLowerCase() - ); +function mountIndeterminateComponent( + _current, + workInProgress, + Component, + renderLanes +) { + if (_current !== null) { + // An indeterminate component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - break; - } + workInProgress.flags |= Placement; + } - default: - error( - '"%s" is not a supported revealOrder on . ' + - 'Did you mean "together", "forwards" or "backwards"?', - revealOrder - ); + var props = workInProgress.pendingProps; + var context; - break; - } - } else { - error( - "%s is not a supported value for revealOrder on . " + - 'Did you mean "together", "forwards" or "backwards"?', - revealOrder - ); - } - } + { + var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); + context = getMaskedContext(workInProgress, unmaskedContext); } -} -function validateTailOptions(tailMode, revealOrder) { + prepareToReadContext(workInProgress, renderLanes); + var value; + { - if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { - if (tailMode !== "collapsed" && tailMode !== "hidden") { - didWarnAboutTailOptions[tailMode] = true; + if ( + Component.prototype && + typeof Component.prototype.render === "function" + ) { + var componentName = getComponentNameFromType(Component) || "Unknown"; + if (!didWarnAboutBadClass[componentName]) { error( - '"%s" is not a supported value for tail on . ' + - 'Did you mean "collapsed" or "hidden"?', - tailMode + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + + "This is likely to cause errors. Change %s to extend React.Component instead.", + componentName, + componentName ); - } else if (revealOrder !== "forwards" && revealOrder !== "backwards") { - didWarnAboutTailOptions[tailMode] = true; - error( - ' is only valid if revealOrder is ' + - '"forwards" or "backwards". ' + - 'Did you mean to specify revealOrder="forwards"?', - tailMode - ); + didWarnAboutBadClass[componentName] = true; } } - } -} - -function validateSuspenseListNestedChild(childSlot, index) { - { - var isAnArray = isArray(childSlot); - var isIterable = - !isAnArray && typeof getIteratorFn(childSlot) === "function"; - - if (isAnArray || isIterable) { - var type = isAnArray ? "array" : "iterable"; - - error( - "A nested %s was passed to row #%s in . Wrap it in " + - "an additional SuspenseList to configure its revealOrder: " + - " ... " + - "{%s} ... " + - "", - type, - index, - type - ); - return false; + if (workInProgress.mode & StrictLegacyMode) { + ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); } - } - return true; -} + setIsRendering(true); + ReactCurrentOwner$1.current = workInProgress; + value = renderWithHooks( + null, + workInProgress, + Component, + props, + context, + renderLanes + ); + setIsRendering(false); + } // React DevTools reads this flag. + + workInProgress.flags |= PerformedWork; -function validateSuspenseListChildren(children, revealOrder) { { + // Support for module components is deprecated and is removed behind a flag. + // Whether or not it would crash later, we want to show a good message in DEV first. if ( - (revealOrder === "forwards" || revealOrder === "backwards") && - children !== undefined && - children !== null && - children !== false + typeof value === "object" && + value !== null && + typeof value.render === "function" && + value.$$typeof === undefined ) { - if (isArray(children)) { - for (var i = 0; i < children.length; i++) { - if (!validateSuspenseListNestedChild(children[i], i)) { - return; - } - } - } else { - var iteratorFn = getIteratorFn(children); - - if (typeof iteratorFn === "function") { - var childrenIterator = iteratorFn.call(children); - - if (childrenIterator) { - var step = childrenIterator.next(); - var _i = 0; + var _componentName = getComponentNameFromType(Component) || "Unknown"; - for (; !step.done; step = childrenIterator.next()) { - if (!validateSuspenseListNestedChild(step.value, _i)) { - return; - } + if (!didWarnAboutModulePatternComponent[_componentName]) { + error( + "The <%s /> component appears to be a function component that returns a class instance. " + + "Change %s to a class that extends React.Component instead. " + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + + "cannot be called with `new` by React.", + _componentName, + _componentName, + _componentName + ); - _i++; - } - } - } else { - error( - 'A single row was passed to a . ' + - "This is not useful since it needs multiple rows. " + - "Did you mean to pass multiple children or an array?", - revealOrder - ); - } + didWarnAboutModulePatternComponent[_componentName] = true; } } } -} -function initSuspenseListRenderState( - workInProgress, - isBackwards, - tail, - lastContentRow, - tailMode -) { - var renderState = workInProgress.memoizedState; + if ( + // Run these checks in production only if the flag is off. + // Eventually we'll delete this branch altogether. + typeof value === "object" && + value !== null && + typeof value.render === "function" && + value.$$typeof === undefined + ) { + { + var _componentName2 = getComponentNameFromType(Component) || "Unknown"; - if (renderState === null) { - workInProgress.memoizedState = { - isBackwards: isBackwards, - rendering: null, - renderingStartTime: 0, - last: lastContentRow, - tail: tail, - tailMode: tailMode - }; - } else { - // We can reuse the existing object from previous renders. - renderState.isBackwards = isBackwards; - renderState.rendering = null; - renderState.renderingStartTime = 0; - renderState.last = lastContentRow; - renderState.tail = tail; - renderState.tailMode = tailMode; - } -} // This can end up rendering this component multiple passes. -// The first pass splits the children fibers into two sets. A head and tail. -// We first render the head. If anything is in fallback state, we do another -// pass through beginWork to rerender all children (including the tail) with -// the force suspend context. If the first render didn't have anything in -// in fallback state. Then we render each row in the tail one-by-one. -// That happens in the completeWork phase without going back to beginWork. + if (!didWarnAboutModulePatternComponent[_componentName2]) { + error( + "The <%s /> component appears to be a function component that returns a class instance. " + + "Change %s to a class that extends React.Component instead. " + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + + "cannot be called with `new` by React.", + _componentName2, + _componentName2, + _componentName2 + ); + + didWarnAboutModulePatternComponent[_componentName2] = true; + } + } // Proceed under the assumption that this is a class instance + + workInProgress.tag = ClassComponent; // Throw out any hooks that were used. + + workInProgress.memoizedState = null; + workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. -function updateSuspenseListComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; - var revealOrder = nextProps.revealOrder; - var tailMode = nextProps.tail; - var newChildren = nextProps.children; - validateRevealOrder(revealOrder); - validateTailOptions(tailMode, revealOrder); - validateSuspenseListChildren(newChildren, revealOrder); - reconcileChildren(current, workInProgress, newChildren, renderLanes); - var suspenseContext = suspenseStackCursor.current; - var shouldForceFallback = hasSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); + var hasContext = false; - if (shouldForceFallback) { - suspenseContext = setShallowSuspenseContext( - suspenseContext, - ForceSuspenseFallback + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; + } + + workInProgress.memoizedState = + value.state !== null && value.state !== undefined ? value.state : null; + initializeUpdateQueue(workInProgress); + adoptClassInstance(workInProgress, value); + mountClassInstance(workInProgress, Component, props, renderLanes); + return finishClassComponent( + null, + workInProgress, + Component, + true, + hasContext, + renderLanes ); - workInProgress.flags |= DidCapture; } else { - var didSuspendBefore = - current !== null && (current.flags & DidCapture) !== NoFlags; + // Proceed under the assumption that this is a function component + workInProgress.tag = FunctionComponent; - if (didSuspendBefore) { - // If we previously forced a fallback, we need to schedule work - // on any nested boundaries to let them know to try to render - // again. This is the same as context updating. - propagateSuspenseContextChange( - workInProgress, - workInProgress.child, - renderLanes - ); - } + { + if (workInProgress.mode & StrictLegacyMode) { + disableLogs(); - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - } + try { + value = renderWithHooks( + null, + workInProgress, + Component, + props, + context, + renderLanes + ); + } finally { + reenableLogs(); + } + } + } - pushSuspenseContext(workInProgress, suspenseContext); + reconcileChildren(null, workInProgress, value, renderLanes); - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - // In legacy mode, SuspenseList doesn't work so we just - // use make it a noop by treating it as the default revealOrder. - workInProgress.memoizedState = null; - } else { - switch (revealOrder) { - case "forwards": { - var lastContentRow = findLastContentRow(workInProgress.child); - var tail; + { + validateFunctionComponentInDev(workInProgress, Component); + } - if (lastContentRow === null) { - // The whole list is part of the tail. - // TODO: We could fast path by just rendering the tail now. - tail = workInProgress.child; - workInProgress.child = null; - } else { - // Disconnect the tail rows after the content row. - // We're going to render them separately later. - tail = lastContentRow.sibling; - lastContentRow.sibling = null; - } + return workInProgress.child; + } +} - initSuspenseListRenderState( - workInProgress, - false, // isBackwards - tail, - lastContentRow, - tailMode +function validateFunctionComponentInDev(workInProgress, Component) { + { + if (Component) { + if (Component.childContextTypes) { + error( + "%s(...): childContextTypes cannot be defined on a function component.", + Component.displayName || Component.name || "Component" ); - break; } + } - case "backwards": { - // We're going to find the first row that has existing content. - // At the same time we're going to reverse the list of everything - // we pass in the meantime. That's going to be our tail in reverse - // order. - var _tail = null; - var row = workInProgress.child; - workInProgress.child = null; + if (workInProgress.ref !== null) { + var info = ""; + var ownerName = getCurrentFiberOwnerNameInDevOrNull(); - while (row !== null) { - var currentRow = row.alternate; // New rows can't be content rows. + if (ownerName) { + info += "\n\nCheck the render method of `" + ownerName + "`."; + } - if (currentRow !== null && findFirstSuspended(currentRow) === null) { - // This is the beginning of the main content. - workInProgress.child = row; - break; - } + var warningKey = ownerName || ""; + var debugSource = workInProgress._debugSource; - var nextRow = row.sibling; - row.sibling = _tail; - _tail = row; - row = nextRow; - } // TODO: If workInProgress.child is null, we can continue on the tail immediately. + if (debugSource) { + warningKey = debugSource.fileName + ":" + debugSource.lineNumber; + } - initSuspenseListRenderState( - workInProgress, - true, // isBackwards - _tail, - null, // last - tailMode + if (!didWarnAboutFunctionRefs[warningKey]) { + didWarnAboutFunctionRefs[warningKey] = true; + + error( + "Function components cannot be given refs. " + + "Attempts to access this ref will fail. " + + "Did you mean to use React.forwardRef()?%s", + info ); - break; } + } - case "together": { - initSuspenseListRenderState( - workInProgress, - false, // isBackwards - null, // tail - null, // last - undefined + if (typeof Component.getDerivedStateFromProps === "function") { + var _componentName3 = getComponentNameFromType(Component) || "Unknown"; + + if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { + error( + "%s: Function components do not support getDerivedStateFromProps.", + _componentName3 ); - break; - } - default: { - // The default reveal order is the same as not having - // a boundary. - workInProgress.memoizedState = null; + didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; } } - } - return workInProgress.child; -} + if ( + typeof Component.contextType === "object" && + Component.contextType !== null + ) { + var _componentName4 = getComponentNameFromType(Component) || "Unknown"; -function updatePortalComponent(current, workInProgress, renderLanes) { - pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); - var nextChildren = workInProgress.pendingProps; + if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { + error( + "%s: Function components do not support contextType.", + _componentName4 + ); - if (current === null) { - // Portals are special because we don't append the children during mount - // but at commit. Therefore we need to track insertions which the normal - // flow doesn't do during mount. This doesn't happen at the root because - // the root always starts with a "current" with a null child. - // TODO: Consider unifying this with how the root works. - workInProgress.child = reconcileChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - } else { - reconcileChildren(current, workInProgress, nextChildren, renderLanes); + didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; + } + } } +} - return workInProgress.child; +var SUSPENDED_MARKER = { + dehydrated: null, + retryLane: NoLane +}; + +function mountSuspenseOffscreenState(renderLanes) { + return { + baseLanes: renderLanes, + cachePool: getSuspendedCachePool() + }; } -var hasWarnedAboutUsingNoValuePropOnContextProvider = false; +function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { + var cachePool = null; -function updateContextProvider(current, workInProgress, renderLanes) { - var providerType = workInProgress.type; - var context = providerType._context; - var newProps = workInProgress.pendingProps; - var oldProps = workInProgress.memoizedProps; - var newValue = newProps.value; + return { + baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), + cachePool: cachePool + }; +} // TODO: Probably should inline this back - { - if (!("value" in newProps)) { - if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { - hasWarnedAboutUsingNoValuePropOnContextProvider = true; +function shouldRemainOnFallback( + suspenseContext, + current, + workInProgress, + renderLanes +) { + // If we're already showing a fallback, there are cases where we need to + // remain on that fallback regardless of whether the content has resolved. + // For example, SuspenseList coordinates when nested content appears. + if (current !== null) { + var suspenseState = current.memoizedState; - error( - "The `value` prop is required for the ``. Did you misspell it or forget to pass it?" - ); - } + if (suspenseState === null) { + // Currently showing content. Don't hide it, even if ForceSuspenseFallack + // is true. More precise name might be "ForceRemainSuspenseFallback". + // Note: This is a factoring smell. Can't remain on a fallback if there's + // no fallback to remain on. + return false; } + } // Not currently showing content. Consult the Suspense context. - var providerPropTypes = workInProgress.type.propTypes; + return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); +} - if (providerPropTypes) { - checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider"); - } - } +function getRemainingWorkInPrimaryTree(current, renderLanes) { + // TODO: Should not remove render lanes that were pinged during this render + return removeLanes(current.childLanes, renderLanes); +} - pushProvider(workInProgress, context, newValue); +function updateSuspenseComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. { - if (oldProps !== null) { - var oldValue = oldProps.value; + if (shouldSuspend(workInProgress)) { + workInProgress.flags |= DidCapture; + } + } - if (objectIs(oldValue, newValue)) { - // No change. Bailout early if children are the same. - if (oldProps.children === newProps.children && !hasContextChanged()) { - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); - } - } else { - // The context value changed. Search for matching consumers and schedule - // them to update. - propagateContextChange(workInProgress, context, renderLanes); + var suspenseContext = suspenseStackCursor.current; + var showFallback = false; + var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; + + if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { + // Something in this boundary's subtree already suspended. Switch to + // rendering the fallback children. + showFallback = true; + workInProgress.flags &= ~DidCapture; + } else { + // Attempting the main content + if (current === null || current.memoizedState !== null) { + // This is a new mount or this boundary is already showing a fallback state. + // Mark this subtree context as having at least one invisible parent that could + // handle the fallback state. + // Boundaries without fallbacks or should be avoided are not considered since + // they cannot handle preferred fallback states. + if ( + nextProps.fallback !== undefined && + nextProps.unstable_avoidThisFallback !== true + ) { + suspenseContext = addSubtreeSuspenseContext( + suspenseContext, + InvisibleParentSuspenseContext + ); } } } - var newChildren = newProps.children; - reconcileChildren(current, workInProgress, newChildren, renderLanes); - return workInProgress.child; -} - -var hasWarnedAboutUsingContextAsConsumer = false; + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense + // boundary's children. This involves some custom reconcilation logic. Two + // main reasons this is so complicated. + // + // First, Legacy Mode has different semantics for backwards compatibility. The + // primary tree will commit in an inconsistent state, so when we do the + // second pass to render the fallback, we do some exceedingly, uh, clever + // hacks to make that not totally break. Like transferring effects and + // deletions from hidden tree. In Concurrent Mode, it's much simpler, + // because we bailout on the primary tree completely and leave it in its old + // state, no effects. Same as what we do for Offscreen (except that + // Offscreen doesn't have the first render pass). + // + // Second is hydration. During hydration, the Suspense fiber has a slightly + // different layout, where the child points to a dehydrated fragment, which + // contains the DOM rendered by the server. + // + // Third, even if you set all that aside, Suspense is like error boundaries in + // that we first we try to render one tree, and if that fails, we render again + // and switch to a different tree. Like a try/catch block. So we have to track + // which branch we're currently rendering. Ideally we would model this using + // a stack. -function updateContextConsumer(current, workInProgress, renderLanes) { - var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In - // DEV mode, we create a separate object for Context.Consumer that acts - // like a proxy to Context. This proxy object adds unnecessary code in PROD - // so we use the old behaviour (Context.Consumer references Context) to - // reduce size and overhead. The separate object references context via - // a property called "_context", which also gives us the ability to check - // in DEV mode if this property exists or not and warn if it does not. + if (current === null) { + // Initial mount + // If we're currently hydrating, try to hydrate this boundary. + // But only if this has a fallback. + if (nextProps.fallback !== undefined); - { - if (context._context === undefined) { - // This may be because it's a Context (rather than a Consumer). - // Or it may be because it's older React where they're the same thing. - // We only want to warn if we're sure it's a new React. - if (context !== context.Consumer) { - if (!hasWarnedAboutUsingContextAsConsumer) { - hasWarnedAboutUsingContextAsConsumer = true; + var nextPrimaryChildren = nextProps.children; + var nextFallbackChildren = nextProps.fallback; - error( - "Rendering directly is not supported and will be removed in " + - "a future major release. Did you mean to render instead?" - ); - } - } - } else { - context = context._context; - } - } + if (showFallback) { + var fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); + var primaryChildFragment = workInProgress.child; + primaryChildFragment.memoizedState = mountSuspenseOffscreenState( + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return fallbackFragment; + } else if (typeof nextProps.unstable_expectedLoadTime === "number") { + // This is a CPU-bound tree. Skip this tree and show a placeholder to + // unblock the surrounding content. Then immediately retry after the + // initial commit. + var _fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); - var newProps = workInProgress.pendingProps; - var render = newProps.children; + var _primaryChildFragment = workInProgress.child; + _primaryChildFragment.memoizedState = mountSuspenseOffscreenState( + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; // Since nothing actually suspended, there will nothing to ping this to + // get it started back up to attempt the next item. While in terms of + // priority this work has the same priority as this current render, it's + // not part of the same transition once the transition has committed. If + // it's sync, we still want to yield so that it can be painted. + // Conceptually, this is really the same as pinging. We can use any + // RetryLane even if it's the one currently rendering since we're leaving + // it behind on this node. - { - if (typeof render !== "function") { - error( - "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." + workInProgress.lanes = SomeRetryLane; + return _fallbackFragment; + } else { + return mountSuspensePrimaryChildren( + workInProgress, + nextPrimaryChildren, + renderLanes ); } - } + } else { + // This is an update. + // If the current fiber has a SuspenseState, that means it's already showing + // a fallback. + var prevState = current.memoizedState; - prepareToReadContext(workInProgress, renderLanes); - var newValue = readContext(context); - var newChildren; + if (prevState !== null) { + if (showFallback) { + var _nextFallbackChildren2 = nextProps.fallback; + var _nextPrimaryChildren2 = nextProps.children; - { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - newChildren = render(newValue); - setIsRendering(false); - } // React DevTools reads this flag. + var _fallbackChildFragment = updateSuspenseFallbackChildren( + current, + workInProgress, + _nextPrimaryChildren2, + _nextFallbackChildren2, + renderLanes + ); - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, newChildren, renderLanes); - return workInProgress.child; -} + var _primaryChildFragment3 = workInProgress.child; + var prevOffscreenState = current.child.memoizedState; + _primaryChildFragment3.memoizedState = + prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); + _primaryChildFragment3.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return _fallbackChildFragment; + } else { + var _nextPrimaryChildren3 = nextProps.children; -function markWorkInProgressReceivedUpdate() { - didReceiveUpdate = true; -} + var _primaryChildFragment4 = updateSuspensePrimaryChildren( + current, + workInProgress, + _nextPrimaryChildren3, + renderLanes + ); -function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { - if (current !== null) { - // Reuse previous dependencies - workInProgress.dependencies = current.dependencies; - } + workInProgress.memoizedState = null; + return _primaryChildFragment4; + } + } else { + // The current tree is not already showing a fallback. + if (showFallback) { + // Timed out. + var _nextFallbackChildren3 = nextProps.fallback; + var _nextPrimaryChildren4 = nextProps.children; + + var _fallbackChildFragment2 = updateSuspenseFallbackChildren( + current, + workInProgress, + _nextPrimaryChildren4, + _nextFallbackChildren3, + renderLanes + ); - { - // Don't update "base" render times for bailouts. - stopProfilerTimerIfRunning(); - } + var _primaryChildFragment5 = workInProgress.child; + var _prevOffscreenState = current.child.memoizedState; + _primaryChildFragment5.memoizedState = + _prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(_prevOffscreenState, renderLanes); + _primaryChildFragment5.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes + ); // Skip the primary children, and continue working on the + // fallback children. - markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. + workInProgress.memoizedState = SUSPENDED_MARKER; + return _fallbackChildFragment2; + } else { + // Still haven't timed out. Continue rendering the children, like we + // normally do. + var _nextPrimaryChildren5 = nextProps.children; - if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { - // The children don't have any work either. We can skip them. - // TODO: Once we add back resuming, we should check if the children are - // a work-in-progress set. If so, we need to transfer their effects. - { - return null; + var _primaryChildFragment6 = updateSuspensePrimaryChildren( + current, + workInProgress, + _nextPrimaryChildren5, + renderLanes + ); + + workInProgress.memoizedState = null; + return _primaryChildFragment6; + } } - } // This fiber doesn't have work, but its subtree does. Clone the child - // fibers and continue. + } +} - cloneChildFibers(current, workInProgress); - return workInProgress.child; +function mountSuspensePrimaryChildren( + workInProgress, + primaryChildren, + renderLanes +) { + var mode = workInProgress.mode; + var primaryChildProps = { + mode: "visible", + children: primaryChildren + }; + var primaryChildFragment = createFiberFromOffscreen( + primaryChildProps, + mode, + renderLanes, + null + ); + primaryChildFragment.return = workInProgress; + workInProgress.child = primaryChildFragment; + return primaryChildFragment; } -function remountFiber(current, oldWorkInProgress, newWorkInProgress) { - { - var returnFiber = oldWorkInProgress.return; +function mountSuspenseFallbackChildren( + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + var mode = workInProgress.mode; + var progressedPrimaryFragment = workInProgress.child; + var primaryChildProps = { + mode: "hidden", + children: primaryChildren + }; + var primaryChildFragment; + var fallbackChildFragment; - if (returnFiber === null) { - throw new Error("Cannot swap the root fiber."); - } // Disconnect from the old current. - // It will get deleted. + if ( + (mode & ConcurrentMode) === NoMode && + progressedPrimaryFragment !== null + ) { + // In legacy mode, we commit the primary tree as if it successfully + // completed, even though it's in an inconsistent state. + primaryChildFragment = progressedPrimaryFragment; + primaryChildFragment.childLanes = NoLanes; + primaryChildFragment.pendingProps = primaryChildProps; - current.alternate = null; - oldWorkInProgress.alternate = null; // Connect to the new tree. + if (workInProgress.mode & ProfileMode) { + // Reset the durations from the first pass so they aren't included in the + // final amounts. This seems counterintuitive, since we're intentionally + // not measuring part of the render phase, but this makes it match what we + // do in Concurrent Mode. + primaryChildFragment.actualDuration = 0; + primaryChildFragment.actualStartTime = -1; + primaryChildFragment.selfBaseDuration = 0; + primaryChildFragment.treeBaseDuration = 0; + } - newWorkInProgress.index = oldWorkInProgress.index; - newWorkInProgress.sibling = oldWorkInProgress.sibling; - newWorkInProgress.return = oldWorkInProgress.return; - newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); + } else { + primaryChildFragment = createFiberFromOffscreen( + primaryChildProps, + mode, + NoLanes, + null + ); + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); + } - if (oldWorkInProgress === returnFiber.child) { - returnFiber.child = newWorkInProgress; - } else { - var prevSibling = returnFiber.child; + primaryChildFragment.return = workInProgress; + fallbackChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; +} - if (prevSibling === null) { - throw new Error("Expected parent to have a child."); - } +function createWorkInProgressOffscreenFiber(current, offscreenProps) { + // The props argument to `createWorkInProgress` is `any` typed, so we use this + // wrapper function to constrain it. + return createWorkInProgress(current, offscreenProps); +} - while (prevSibling.sibling !== oldWorkInProgress) { - prevSibling = prevSibling.sibling; +function updateSuspensePrimaryChildren( + current, + workInProgress, + primaryChildren, + renderLanes +) { + var currentPrimaryChildFragment = current.child; + var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; + var primaryChildFragment = createWorkInProgressOffscreenFiber( + currentPrimaryChildFragment, + { + mode: "visible", + children: primaryChildren + } + ); - if (prevSibling === null) { - throw new Error("Expected to find the previous sibling."); - } - } + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + primaryChildFragment.lanes = renderLanes; + } - prevSibling.sibling = newWorkInProgress; - } // Delete the old fiber and place the new one. - // Since the old fiber is disconnected, we have to schedule it manually. + primaryChildFragment.return = workInProgress; + primaryChildFragment.sibling = null; - var deletions = returnFiber.deletions; + if (currentFallbackChildFragment !== null) { + // Delete the fallback child fragment + var deletions = workInProgress.deletions; if (deletions === null) { - returnFiber.deletions = [current]; - returnFiber.flags |= ChildDeletion; + workInProgress.deletions = [currentFallbackChildFragment]; + workInProgress.flags |= ChildDeletion; } else { - deletions.push(current); + deletions.push(currentFallbackChildFragment); } - - newWorkInProgress.flags |= Placement; // Restart work from the new fiber. - - return newWorkInProgress; } + + workInProgress.child = primaryChildFragment; + return primaryChildFragment; } -function beginWork(current, workInProgress, renderLanes) { - var updateLanes = workInProgress.lanes; +function updateSuspenseFallbackChildren( + current, + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + var mode = workInProgress.mode; + var currentPrimaryChildFragment = current.child; + var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; + var primaryChildProps = { + mode: "hidden", + children: primaryChildren + }; + var primaryChildFragment; - { - if (workInProgress._debugNeedsRemount && current !== null) { - // This will restart the begin phase with a new fiber. - return remountFiber( - current, - workInProgress, - createFiberFromTypeAndProps( - workInProgress.type, - workInProgress.key, - workInProgress.pendingProps, - workInProgress._debugOwner || null, - workInProgress.mode, - workInProgress.lanes - ) - ); - } - } + if ( + // In legacy mode, we commit the primary tree as if it successfully + // completed, even though it's in an inconsistent state. + (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was + // already cloned. In legacy mode, the only case where this isn't true is + // when DevTools forces us to display a fallback; we skip the first render + // pass entirely and go straight to rendering the fallback. (In Concurrent + // Mode, SuspenseList can also trigger this scenario, but this is a legacy- + // only codepath.) + workInProgress.child !== currentPrimaryChildFragment + ) { + var progressedPrimaryFragment = workInProgress.child; + primaryChildFragment = progressedPrimaryFragment; + primaryChildFragment.childLanes = NoLanes; + primaryChildFragment.pendingProps = primaryChildProps; - if (current !== null) { - var oldProps = current.memoizedProps; - var newProps = workInProgress.pendingProps; + if (workInProgress.mode & ProfileMode) { + // Reset the durations from the first pass so they aren't included in the + // final amounts. This seems counterintuitive, since we're intentionally + // not measuring part of the render phase, but this makes it match what we + // do in Concurrent Mode. + primaryChildFragment.actualDuration = 0; + primaryChildFragment.actualStartTime = -1; + primaryChildFragment.selfBaseDuration = + currentPrimaryChildFragment.selfBaseDuration; + primaryChildFragment.treeBaseDuration = + currentPrimaryChildFragment.treeBaseDuration; + } // The fallback fiber was added as a deletion during the first pass. + // However, since we're going to remain on the fallback, we no longer want + // to delete it. - if ( - oldProps !== newProps || - hasContextChanged() || // Force a re-render if the implementation changed due to hot reload: - workInProgress.type !== current.type - ) { - // If props or context changed, mark the fiber as having performed work. - // This may be unset if the props are determined to be equal later (memo). - didReceiveUpdate = true; - } else if (!includesSomeLane(renderLanes, updateLanes)) { - didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering - // the begin phase. There's still some bookkeeping we that needs to be done - // in this optimized path, mostly pushing stuff onto the stack. + workInProgress.deletions = null; + } else { + primaryChildFragment = createWorkInProgressOffscreenFiber( + currentPrimaryChildFragment, + primaryChildProps + ); // Since we're reusing a current tree, we need to reuse the flags, too. + // (We don't do this in legacy mode, because in legacy mode we don't re-use + // the current tree; see previous branch.) - switch (workInProgress.tag) { - case HostRoot: - pushHostRootContext(workInProgress); - break; + primaryChildFragment.subtreeFlags = + currentPrimaryChildFragment.subtreeFlags & StaticMask; + } - case HostComponent: - pushHostContext(workInProgress); - break; + var fallbackChildFragment; - case ClassComponent: { - var Component = workInProgress.type; + if (currentFallbackChildFragment !== null) { + fallbackChildFragment = createWorkInProgress( + currentFallbackChildFragment, + fallbackChildren + ); + } else { + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); // Needs a placement effect because the parent (the Suspense boundary) already + // mounted but this is a new fiber. - if (isContextProvider(Component)) { - pushContextProvider(workInProgress); - } + fallbackChildFragment.flags |= Placement; + } - break; - } + fallbackChildFragment.return = workInProgress; + primaryChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; +} - case HostPortal: - pushHostContainer( - workInProgress, - workInProgress.stateNode.containerInfo - ); - break; +function scheduleWorkOnFiber(fiber, renderLanes) { + fiber.lanes = mergeLanes(fiber.lanes, renderLanes); + var alternate = fiber.alternate; - case ContextProvider: { - var newValue = workInProgress.memoizedProps.value; - var context = workInProgress.type._context; - pushProvider(workInProgress, context, newValue); - break; - } + if (alternate !== null) { + alternate.lanes = mergeLanes(alternate.lanes, renderLanes); + } - case Profiler: - { - // Profiler should only call onRender when one of its descendants actually rendered. - var hasChildWork = includesSomeLane( - renderLanes, - workInProgress.childLanes - ); + scheduleWorkOnParentPath(fiber.return, renderLanes); +} - if (hasChildWork) { - workInProgress.flags |= Update; - } - } +function propagateSuspenseContextChange( + workInProgress, + firstChild, + renderLanes +) { + // Mark any Suspense boundaries with fallbacks as having work to do. + // If they were previously forced into fallbacks, they may now be able + // to unblock. + var node = firstChild; - break; + while (node !== null) { + if (node.tag === SuspenseComponent) { + var state = node.memoizedState; - case SuspenseComponent: { - var state = workInProgress.memoizedState; + if (state !== null) { + scheduleWorkOnFiber(node, renderLanes); + } + } else if (node.tag === SuspenseListComponent) { + // If the tail is hidden there might not be an Suspense boundaries + // to schedule work on. In this case we have to schedule it on the + // list itself. + // We don't have to traverse to the children of the list since + // the list will propagate the change when it rerenders. + scheduleWorkOnFiber(node, renderLanes); + } else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; + } - if (state !== null) { - // whether to retry the primary children, or to skip over it and - // go straight to the fallback. Check the priority of the primary - // child fragment. + if (node === workInProgress) { + return; + } - var primaryChildFragment = workInProgress.child; - var primaryChildLanes = primaryChildFragment.childLanes; + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; + } - if (includesSomeLane(renderLanes, primaryChildLanes)) { - // The primary children have pending work. Use the normal path - // to attempt to render the primary children again. - return updateSuspenseComponent( - current, - workInProgress, - renderLanes - ); - } else { - // The primary child fragment does not have pending work marked - // on it - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ); // The primary children do not have pending work with sufficient - // priority. Bailout. + node = node.return; + } - var child = bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); + node.sibling.return = node.return; + node = node.sibling; + } +} - if (child !== null) { - // The fallback children have pending work. Skip over the - // primary children and work on the fallback. - return child.sibling; - } else { - // Note: We can return `null` here because we already checked - // whether there were nested context consumers, via the call to - // `bailoutOnAlreadyFinishedWork` above. - return null; - } - } - } else { - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ); - } +function findLastContentRow(firstChild) { + // This is going to find the last row among these children that is already + // showing content on the screen, as opposed to being in fallback state or + // new. If a row has multiple Suspense boundaries, any of them being in the + // fallback state, counts as the whole row being in a fallback state. + // Note that the "rows" will be workInProgress, but any nested children + // will still be current since we haven't rendered them yet. The mounted + // order may not be the same as the new order. We use the new order. + var row = firstChild; + var lastContentRow = null; - break; - } + while (row !== null) { + var currentRow = row.alternate; // New rows can't be content rows. - case SuspenseListComponent: { - var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; + if (currentRow !== null && findFirstSuspended(currentRow) === null) { + lastContentRow = row; + } - var _hasChildWork = includesSomeLane( - renderLanes, - workInProgress.childLanes - ); + row = row.sibling; + } - if (didSuspendBefore) { - if (_hasChildWork) { - // If something was in fallback state last time, and we have all the - // same children then we're still in progressive loading state. - // Something might get unblocked by state updates or retries in the - // tree which will affect the tail. So we need to use the normal - // path to compute the correct tail. - return updateSuspenseListComponent( - current, - workInProgress, - renderLanes - ); - } // If none of the children had any work, that means that none of - // them got retried so they'll still be blocked in the same way - // as before. We can fast bail out. + return lastContentRow; +} - workInProgress.flags |= DidCapture; - } // If nothing suspended before and we're rendering the same children, - // then the tail doesn't matter. Anything new that suspends will work - // in the "together" mode, so we can continue from the state we had. +function validateRevealOrder(revealOrder) { + { + if ( + revealOrder !== undefined && + revealOrder !== "forwards" && + revealOrder !== "backwards" && + revealOrder !== "together" && + !didWarnAboutRevealOrder[revealOrder] + ) { + didWarnAboutRevealOrder[revealOrder] = true; - var renderState = workInProgress.memoizedState; + if (typeof revealOrder === "string") { + switch (revealOrder.toLowerCase()) { + case "together": + case "forwards": + case "backwards": { + error( + '"%s" is not a valid value for revealOrder on . ' + + 'Use lowercase "%s" instead.', + revealOrder, + revealOrder.toLowerCase() + ); - if (renderState !== null) { - // Reset to the "together" mode in case we've started a different - // update in the past but didn't complete it. - renderState.rendering = null; - renderState.tail = null; - renderState.lastEffect = null; + break; } - pushSuspenseContext(workInProgress, suspenseStackCursor.current); + case "forward": + case "backward": { + error( + '"%s" is not a valid value for revealOrder on . ' + + 'React uses the -s suffix in the spelling. Use "%ss" instead.', + revealOrder, + revealOrder.toLowerCase() + ); - if (_hasChildWork) { break; - } else { - // If none of the children had any work, that means that none of - // them got retried so they'll still be blocked in the same way - // as before. We can fast bail out. - return null; } - } - case OffscreenComponent: - case LegacyHiddenComponent: { - // Need to check if the tree still needs to be deferred. This is - // almost identical to the logic used in the normal update path, - // so we'll just enter that. The only difference is we'll bail out - // at the next level instead of this one, because the child props - // have not changed. Which is fine. - // TODO: Probably should refactor `beginWork` to split the bailout - // path from the normal path. I'm tempted to do a labeled break here - // but I won't :) - workInProgress.lanes = NoLanes; - return updateOffscreenComponent(current, workInProgress, renderLanes); - } - } + default: + error( + '"%s" is not a supported revealOrder on . ' + + 'Did you mean "together", "forwards" or "backwards"?', + revealOrder + ); - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } else { - if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { - // This is a special case that only exists for legacy mode. - // See https://github.com/facebook/react/pull/19216. - didReceiveUpdate = true; + break; + } } else { - // An update was scheduled on this fiber, but there are no new props - // nor legacy context. Set this to false. If an update queue or context - // consumer produces a changed value, it will set this to true. Otherwise, - // the component will assume the children have not changed and bail out. - didReceiveUpdate = false; + error( + "%s is not a supported value for revealOrder on . " + + 'Did you mean "together", "forwards" or "backwards"?', + revealOrder + ); } } - } else { - didReceiveUpdate = false; - } // Before entering the begin phase, clear pending update priority. - // TODO: This assumes that we're about to evaluate the component and process - // the update queue. However, there's an exception: SimpleMemoComponent - // sometimes bails out later in the begin phase. This indicates that we should - // move this assignment out of the common path and into each branch. + } +} - workInProgress.lanes = NoLanes; +function validateTailOptions(tailMode, revealOrder) { + { + if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { + if (tailMode !== "collapsed" && tailMode !== "hidden") { + didWarnAboutTailOptions[tailMode] = true; - switch (workInProgress.tag) { - case IndeterminateComponent: { - return mountIndeterminateComponent( - current, - workInProgress, - workInProgress.type, - renderLanes - ); + error( + '"%s" is not a supported value for tail on . ' + + 'Did you mean "collapsed" or "hidden"?', + tailMode + ); + } else if (revealOrder !== "forwards" && revealOrder !== "backwards") { + didWarnAboutTailOptions[tailMode] = true; + + error( + ' is only valid if revealOrder is ' + + '"forwards" or "backwards". ' + + 'Did you mean to specify revealOrder="forwards"?', + tailMode + ); + } } + } +} - case LazyComponent: { - var elementType = workInProgress.elementType; - return mountLazyComponent( - current, - workInProgress, - elementType, - updateLanes, - renderLanes +function validateSuspenseListNestedChild(childSlot, index) { + { + var isAnArray = isArray(childSlot); + var isIterable = + !isAnArray && typeof getIteratorFn(childSlot) === "function"; + + if (isAnArray || isIterable) { + var type = isAnArray ? "array" : "iterable"; + + error( + "A nested %s was passed to row #%s in . Wrap it in " + + "an additional SuspenseList to configure its revealOrder: " + + " ... " + + "{%s} ... " + + "", + type, + index, + type ); + + return false; } + } - case FunctionComponent: { - var _Component = workInProgress.type; - var unresolvedProps = workInProgress.pendingProps; - var resolvedProps = - workInProgress.elementType === _Component - ? unresolvedProps - : resolveDefaultProps(_Component, unresolvedProps); - return updateFunctionComponent( - current, - workInProgress, - _Component, - resolvedProps, - renderLanes - ); + return true; +} + +function validateSuspenseListChildren(children, revealOrder) { + { + if ( + (revealOrder === "forwards" || revealOrder === "backwards") && + children !== undefined && + children !== null && + children !== false + ) { + if (isArray(children)) { + for (var i = 0; i < children.length; i++) { + if (!validateSuspenseListNestedChild(children[i], i)) { + return; + } + } + } else { + var iteratorFn = getIteratorFn(children); + + if (typeof iteratorFn === "function") { + var childrenIterator = iteratorFn.call(children); + + if (childrenIterator) { + var step = childrenIterator.next(); + var _i = 0; + + for (; !step.done; step = childrenIterator.next()) { + if (!validateSuspenseListNestedChild(step.value, _i)) { + return; + } + + _i++; + } + } + } else { + error( + 'A single row was passed to a . ' + + "This is not useful since it needs multiple rows. " + + "Did you mean to pass multiple children or an array?", + revealOrder + ); + } + } } + } +} - case ClassComponent: { - var _Component2 = workInProgress.type; - var _unresolvedProps = workInProgress.pendingProps; +function initSuspenseListRenderState( + workInProgress, + isBackwards, + tail, + lastContentRow, + tailMode +) { + var renderState = workInProgress.memoizedState; - var _resolvedProps = - workInProgress.elementType === _Component2 - ? _unresolvedProps - : resolveDefaultProps(_Component2, _unresolvedProps); + if (renderState === null) { + workInProgress.memoizedState = { + isBackwards: isBackwards, + rendering: null, + renderingStartTime: 0, + last: lastContentRow, + tail: tail, + tailMode: tailMode + }; + } else { + // We can reuse the existing object from previous renders. + renderState.isBackwards = isBackwards; + renderState.rendering = null; + renderState.renderingStartTime = 0; + renderState.last = lastContentRow; + renderState.tail = tail; + renderState.tailMode = tailMode; + } +} // This can end up rendering this component multiple passes. +// The first pass splits the children fibers into two sets. A head and tail. +// We first render the head. If anything is in fallback state, we do another +// pass through beginWork to rerender all children (including the tail) with +// the force suspend context. If the first render didn't have anything in +// in fallback state. Then we render each row in the tail one-by-one. +// That happens in the completeWork phase without going back to beginWork. - return updateClassComponent( - current, +function updateSuspenseListComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; + var revealOrder = nextProps.revealOrder; + var tailMode = nextProps.tail; + var newChildren = nextProps.children; + validateRevealOrder(revealOrder); + validateTailOptions(tailMode, revealOrder); + validateSuspenseListChildren(newChildren, revealOrder); + reconcileChildren(current, workInProgress, newChildren, renderLanes); + var suspenseContext = suspenseStackCursor.current; + var shouldForceFallback = hasSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + + if (shouldForceFallback) { + suspenseContext = setShallowSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + workInProgress.flags |= DidCapture; + } else { + var didSuspendBefore = + current !== null && (current.flags & DidCapture) !== NoFlags; + + if (didSuspendBefore) { + // If we previously forced a fallback, we need to schedule work + // on any nested boundaries to let them know to try to render + // again. This is the same as context updating. + propagateSuspenseContextChange( workInProgress, - _Component2, - _resolvedProps, + workInProgress.child, renderLanes ); } - case HostRoot: - return updateHostRoot(current, workInProgress, renderLanes); + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + } - case HostComponent: - return updateHostComponent(current, workInProgress, renderLanes); + pushSuspenseContext(workInProgress, suspenseContext); - case HostText: - return updateHostText(); + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + // In legacy mode, SuspenseList doesn't work so we just + // use make it a noop by treating it as the default revealOrder. + workInProgress.memoizedState = null; + } else { + switch (revealOrder) { + case "forwards": { + var lastContentRow = findLastContentRow(workInProgress.child); + var tail; - case SuspenseComponent: - return updateSuspenseComponent(current, workInProgress, renderLanes); + if (lastContentRow === null) { + // The whole list is part of the tail. + // TODO: We could fast path by just rendering the tail now. + tail = workInProgress.child; + workInProgress.child = null; + } else { + // Disconnect the tail rows after the content row. + // We're going to render them separately later. + tail = lastContentRow.sibling; + lastContentRow.sibling = null; + } - case HostPortal: - return updatePortalComponent(current, workInProgress, renderLanes); + initSuspenseListRenderState( + workInProgress, + false, // isBackwards + tail, + lastContentRow, + tailMode + ); + break; + } - case ForwardRef: { - var type = workInProgress.type; - var _unresolvedProps2 = workInProgress.pendingProps; + case "backwards": { + // We're going to find the first row that has existing content. + // At the same time we're going to reverse the list of everything + // we pass in the meantime. That's going to be our tail in reverse + // order. + var _tail = null; + var row = workInProgress.child; + workInProgress.child = null; - var _resolvedProps2 = - workInProgress.elementType === type - ? _unresolvedProps2 - : resolveDefaultProps(type, _unresolvedProps2); + while (row !== null) { + var currentRow = row.alternate; // New rows can't be content rows. - return updateForwardRef( - current, - workInProgress, - type, - _resolvedProps2, - renderLanes - ); - } + if (currentRow !== null && findFirstSuspended(currentRow) === null) { + // This is the beginning of the main content. + workInProgress.child = row; + break; + } - case Fragment: - return updateFragment(current, workInProgress, renderLanes); + var nextRow = row.sibling; + row.sibling = _tail; + _tail = row; + row = nextRow; + } // TODO: If workInProgress.child is null, we can continue on the tail immediately. - case Mode: - return updateMode(current, workInProgress, renderLanes); + initSuspenseListRenderState( + workInProgress, + true, // isBackwards + _tail, + null, // last + tailMode + ); + break; + } - case Profiler: - return updateProfiler(current, workInProgress, renderLanes); + case "together": { + initSuspenseListRenderState( + workInProgress, + false, // isBackwards + null, // tail + null, // last + undefined + ); + break; + } - case ContextProvider: - return updateContextProvider(current, workInProgress, renderLanes); + default: { + // The default reveal order is the same as not having + // a boundary. + workInProgress.memoizedState = null; + } + } + } - case ContextConsumer: - return updateContextConsumer(current, workInProgress, renderLanes); + return workInProgress.child; +} - case MemoComponent: { - var _type2 = workInProgress.type; - var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. +function updatePortalComponent(current, workInProgress, renderLanes) { + pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); + var nextChildren = workInProgress.pendingProps; - var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); + if (current === null) { + // Portals are special because we don't append the children during mount + // but at commit. Therefore we need to track insertions which the normal + // flow doesn't do during mount. This doesn't happen at the root because + // the root always starts with a "current" with a null child. + // TODO: Consider unifying this with how the root works. + workInProgress.child = reconcileChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); + } else { + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } - { - if (workInProgress.type !== workInProgress.elementType) { - var outerPropTypes = _type2.propTypes; + return workInProgress.child; +} - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - _resolvedProps3, // Resolved for outer only - "prop", - getComponentNameFromType(_type2) - ); - } - } +var hasWarnedAboutUsingNoValuePropOnContextProvider = false; + +function updateContextProvider(current, workInProgress, renderLanes) { + var providerType = workInProgress.type; + var context = providerType._context; + var newProps = workInProgress.pendingProps; + var oldProps = workInProgress.memoizedProps; + var newValue = newProps.value; + + { + if (!("value" in newProps)) { + if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { + hasWarnedAboutUsingNoValuePropOnContextProvider = true; + + error( + "The `value` prop is required for the ``. Did you misspell it or forget to pass it?" + ); } + } - _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); - return updateMemoComponent( - current, - workInProgress, - _type2, - _resolvedProps3, - updateLanes, - renderLanes - ); + var providerPropTypes = workInProgress.type.propTypes; + + if (providerPropTypes) { + checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider"); } + } - case SimpleMemoComponent: { - return updateSimpleMemoComponent( - current, - workInProgress, - workInProgress.type, - workInProgress.pendingProps, - updateLanes, - renderLanes - ); + pushProvider(workInProgress, context, newValue); + + { + if (oldProps !== null) { + var oldValue = oldProps.value; + + if (objectIs(oldValue, newValue)) { + // No change. Bailout early if children are the same. + if (oldProps.children === newProps.children && !hasContextChanged()) { + return bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); + } + } else { + // The context value changed. Search for matching consumers and schedule + // them to update. + propagateContextChange(workInProgress, context, renderLanes); + } } + } - case IncompleteClassComponent: { - var _Component3 = workInProgress.type; - var _unresolvedProps4 = workInProgress.pendingProps; + var newChildren = newProps.children; + reconcileChildren(current, workInProgress, newChildren, renderLanes); + return workInProgress.child; +} - var _resolvedProps4 = - workInProgress.elementType === _Component3 - ? _unresolvedProps4 - : resolveDefaultProps(_Component3, _unresolvedProps4); +var hasWarnedAboutUsingContextAsConsumer = false; + +function updateContextConsumer(current, workInProgress, renderLanes) { + var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In + // DEV mode, we create a separate object for Context.Consumer that acts + // like a proxy to Context. This proxy object adds unnecessary code in PROD + // so we use the old behaviour (Context.Consumer references Context) to + // reduce size and overhead. The separate object references context via + // a property called "_context", which also gives us the ability to check + // in DEV mode if this property exists or not and warn if it does not. + + { + if (context._context === undefined) { + // This may be because it's a Context (rather than a Consumer). + // Or it may be because it's older React where they're the same thing. + // We only want to warn if we're sure it's a new React. + if (context !== context.Consumer) { + if (!hasWarnedAboutUsingContextAsConsumer) { + hasWarnedAboutUsingContextAsConsumer = true; + + error( + "Rendering directly is not supported and will be removed in " + + "a future major release. Did you mean to render instead?" + ); + } + } + } else { + context = context._context; + } + } - return mountIncompleteClassComponent( - current, - workInProgress, - _Component3, - _resolvedProps4, - renderLanes + var newProps = workInProgress.pendingProps; + var render = newProps.children; + + { + if (typeof render !== "function") { + error( + "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." ); } + } - case SuspenseListComponent: { - return updateSuspenseListComponent(current, workInProgress, renderLanes); - } + prepareToReadContext(workInProgress, renderLanes); + var newValue = readContext(context); + var newChildren; - case ScopeComponent: { - break; - } + { + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + newChildren = render(newValue); + setIsRendering(false); + } // React DevTools reads this flag. - case OffscreenComponent: { - return updateOffscreenComponent(current, workInProgress, renderLanes); - } + workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, newChildren, renderLanes); + return workInProgress.child; +} - case LegacyHiddenComponent: { - return updateLegacyHiddenComponent(current, workInProgress, renderLanes); - } +function markWorkInProgressReceivedUpdate() { + didReceiveUpdate = true; +} + +function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { + if (current !== null) { + // Reuse previous dependencies + workInProgress.dependencies = current.dependencies; } { - throw Error( - "Unknown unit of work tag (" + - workInProgress.tag + - "). This error is likely caused by a bug in React. Please file an issue." - ); + // Don't update "base" render times for bailouts. + stopProfilerTimerIfRunning(); } -} -function markUpdate(workInProgress) { - // Tag the fiber with an update effect. This turns a Placement into - // a PlacementAndUpdate. - workInProgress.flags |= Update; -} + markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. -function markRef$1(workInProgress) { - workInProgress.flags |= Ref; + if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { + // The children don't have any work either. We can skip them. + // TODO: Once we add back resuming, we should check if the children are + // a work-in-progress set. If so, we need to transfer their effects. + { + return null; + } + } // This fiber doesn't have work, but its subtree does. Clone the child + // fibers and continue. + + cloneChildFibers(current, workInProgress); + return workInProgress.child; } -var appendAllChildren; -var updateHostContainer; -var updateHostComponent$1; -var updateHostText$1; +function remountFiber(current, oldWorkInProgress, newWorkInProgress) { + { + var returnFiber = oldWorkInProgress.return; -{ - // Mutation mode - appendAllChildren = function( - parent, - workInProgress, - needsVisibilityToggle, - isHidden - ) { - // We only have the top Fiber that was created but we need recurse down its - // children to find all the terminal nodes. - var node = workInProgress.child; + if (returnFiber === null) { + throw new Error("Cannot swap the root fiber."); + } // Disconnect from the old current. + // It will get deleted. - while (node !== null) { - if (node.tag === HostComponent || node.tag === HostText) { - appendInitialChild(parent, node.stateNode); - } else if (node.tag === HostPortal); - else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } + current.alternate = null; + oldWorkInProgress.alternate = null; // Connect to the new tree. - if (node === workInProgress) { - return; + newWorkInProgress.index = oldWorkInProgress.index; + newWorkInProgress.sibling = oldWorkInProgress.sibling; + newWorkInProgress.return = oldWorkInProgress.return; + newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. + + if (oldWorkInProgress === returnFiber.child) { + returnFiber.child = newWorkInProgress; + } else { + var prevSibling = returnFiber.child; + + if (prevSibling === null) { + throw new Error("Expected parent to have a child."); } - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; - } + while (prevSibling.sibling !== oldWorkInProgress) { + prevSibling = prevSibling.sibling; - node = node.return; + if (prevSibling === null) { + throw new Error("Expected to find the previous sibling."); + } } - node.sibling.return = node.return; - node = node.sibling; + prevSibling.sibling = newWorkInProgress; + } // Delete the old fiber and place the new one. + // Since the old fiber is disconnected, we have to schedule it manually. + + var deletions = returnFiber.deletions; + + if (deletions === null) { + returnFiber.deletions = [current]; + returnFiber.flags |= ChildDeletion; + } else { + deletions.push(current); } - }; - updateHostContainer = function(current, workInProgress) { - // Noop - }; + newWorkInProgress.flags |= Placement; // Restart work from the new fiber. - updateHostComponent$1 = function( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ) { - // If we have an alternate, that means this is an update and we need to - // schedule a side-effect to do the updates. + return newWorkInProgress; + } +} + +function beginWork(current, workInProgress, renderLanes) { + var updateLanes = workInProgress.lanes; + + { + if (workInProgress._debugNeedsRemount && current !== null) { + // This will restart the begin phase with a new fiber. + return remountFiber( + current, + workInProgress, + createFiberFromTypeAndProps( + workInProgress.type, + workInProgress.key, + workInProgress.pendingProps, + workInProgress._debugOwner || null, + workInProgress.mode, + workInProgress.lanes + ) + ); + } + } + + if (current !== null) { var oldProps = current.memoizedProps; + var newProps = workInProgress.pendingProps; - if (oldProps === newProps) { - // In mutation mode, this is sufficient for a bailout because - // we won't touch this node even if children changed. - return; - } // If we get updated because one of our children updated, we don't - // have newProps so we'll have to reuse them. - // TODO: Split the update API as separate for the props vs. children. - // Even better would be if children weren't special cased at all tho. + if ( + oldProps !== newProps || + hasContextChanged() || // Force a re-render if the implementation changed due to hot reload: + workInProgress.type !== current.type + ) { + // If props or context changed, mark the fiber as having performed work. + // This may be unset if the props are determined to be equal later (memo). + didReceiveUpdate = true; + } else if (!includesSomeLane(renderLanes, updateLanes)) { + didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering + // the begin phase. There's still some bookkeeping we that needs to be done + // in this optimized path, mostly pushing stuff onto the stack. - 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`. + switch (workInProgress.tag) { + case HostRoot: + pushHostRootContext(workInProgress); + break; - var updatePayload = prepareUpdate(); // TODO: Type this specific to this type of component. + case HostComponent: + pushHostContext(workInProgress); + break; - workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there - // is a new ref we mark this as an update. All the work is done in commitWork. + case ClassComponent: { + var Component = workInProgress.type; - if (updatePayload) { - markUpdate(workInProgress); - } - }; + if (isContextProvider(Component)) { + pushContextProvider(workInProgress); + } - updateHostText$1 = function(current, workInProgress, oldText, newText) { - // If the text differs, mark it as an update. All the work in done in commitWork. - if (oldText !== newText) { - markUpdate(workInProgress); - } - }; -} + break; + } -function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { - switch (renderState.tailMode) { - case "hidden": { - // Any insertions at the end of the tail list after this point - // should be invisible. If there are already mounted boundaries - // anything before them are not considered for collapsing. - // Therefore we need to go through the whole tail to find if - // there are any. - var tailNode = renderState.tail; - var lastTailNode = null; + case HostPortal: + pushHostContainer( + workInProgress, + workInProgress.stateNode.containerInfo + ); + break; - while (tailNode !== null) { - if (tailNode.alternate !== null) { - lastTailNode = tailNode; + case ContextProvider: { + var newValue = workInProgress.memoizedProps.value; + var context = workInProgress.type._context; + pushProvider(workInProgress, context, newValue); + break; } - tailNode = tailNode.sibling; - } // Next we're simply going to delete all insertions after the - // last rendered item. - - if (lastTailNode === null) { - // All remaining items in the tail are insertions. - renderState.tail = null; - } else { - // Detach the insertion after the last node that was already - // inserted. - lastTailNode.sibling = null; - } + case Profiler: + { + // Profiler should only call onRender when one of its descendants actually rendered. + var hasChildWork = includesSomeLane( + renderLanes, + workInProgress.childLanes + ); - break; - } + if (hasChildWork) { + workInProgress.flags |= Update; + } - case "collapsed": { - // Any insertions at the end of the tail list after this point - // should be invisible. If there are already mounted boundaries - // anything before them are not considered for collapsing. - // Therefore we need to go through the whole tail to find if - // there are any. - var _tailNode = renderState.tail; - var _lastTailNode = null; + { + // Reset effect durations for the next eventual effect phase. + // These are reset during render to allow the DevTools commit hook a chance to read them, + var stateNode = workInProgress.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; + } + } - while (_tailNode !== null) { - if (_tailNode.alternate !== null) { - _lastTailNode = _tailNode; - } + break; - _tailNode = _tailNode.sibling; - } // Next we're simply going to delete all insertions after the - // last rendered item. + case SuspenseComponent: { + var state = workInProgress.memoizedState; - if (_lastTailNode === null) { - // All remaining items in the tail are insertions. - if (!hasRenderedATailFallback && renderState.tail !== null) { - // We suspended during the head. We want to show at least one - // row at the tail. So we'll keep on and cut off the rest. - renderState.tail.sibling = null; - } else { - renderState.tail = null; - } - } else { - // Detach the insertion after the last node that was already - // inserted. - _lastTailNode.sibling = null; - } + if (state !== null) { + // whether to retry the primary children, or to skip over it and + // go straight to the fallback. Check the priority of the primary + // child fragment. - break; - } - } -} + var primaryChildFragment = workInProgress.child; + var primaryChildLanes = primaryChildFragment.childLanes; -function bubbleProperties(completedWork) { - var didBailout = - completedWork.alternate !== null && - completedWork.alternate.child === completedWork.child; - var newChildLanes = NoLanes; - var subtreeFlags = NoFlags; + if (includesSomeLane(renderLanes, primaryChildLanes)) { + // The primary children have pending work. Use the normal path + // to attempt to render the primary children again. + return updateSuspenseComponent( + current, + workInProgress, + renderLanes + ); + } else { + // The primary child fragment does not have pending work marked + // on it + pushSuspenseContext( + workInProgress, + setDefaultShallowSuspenseContext(suspenseStackCursor.current) + ); // The primary children do not have pending work with sufficient + // priority. Bailout. - if (!didBailout) { - // Bubble up the earliest expiration time. - if ((completedWork.mode & ProfileMode) !== NoMode) { - // In profiling mode, resetChildExpirationTime is also used to reset - // profiler durations. - var actualDuration = completedWork.actualDuration; - var treeBaseDuration = completedWork.selfBaseDuration; - var child = completedWork.child; + var child = bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); - while (child !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(child.lanes, child.childLanes) - ); - subtreeFlags |= child.subtreeFlags; - subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will - // only be updated if work is done on the fiber (i.e. it doesn't bailout). - // When work is done, it should bubble to the parent's actualDuration. If - // the fiber has not been cloned though, (meaning no work was done), then - // this value will reflect the amount of time spent working on a previous - // render. In that case it should not bubble. We determine whether it was - // cloned by comparing the child pointer. + if (child !== null) { + // The fallback children have pending work. Skip over the + // primary children and work on the fallback. + return child.sibling; + } else { + // Note: We can return `null` here because we already checked + // whether there were nested context consumers, via the call to + // `bailoutOnAlreadyFinishedWork` above. + return null; + } + } + } else { + pushSuspenseContext( + workInProgress, + setDefaultShallowSuspenseContext(suspenseStackCursor.current) + ); + } - actualDuration += child.actualDuration; - treeBaseDuration += child.treeBaseDuration; - child = child.sibling; - } + break; + } - completedWork.actualDuration = actualDuration; - completedWork.treeBaseDuration = treeBaseDuration; - } else { - var _child = completedWork.child; + case SuspenseListComponent: { + var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; - while (_child !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child.lanes, _child.childLanes) - ); - subtreeFlags |= _child.subtreeFlags; - subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code - // smell because it assumes the commit phase is never concurrent with - // the render phase. Will address during refactor to alternate model. + var _hasChildWork = includesSomeLane( + renderLanes, + workInProgress.childLanes + ); - _child.return = completedWork; - _child = _child.sibling; - } - } + if (didSuspendBefore) { + if (_hasChildWork) { + // If something was in fallback state last time, and we have all the + // same children then we're still in progressive loading state. + // Something might get unblocked by state updates or retries in the + // tree which will affect the tail. So we need to use the normal + // path to compute the correct tail. + return updateSuspenseListComponent( + current, + workInProgress, + renderLanes + ); + } // If none of the children had any work, that means that none of + // them got retried so they'll still be blocked in the same way + // as before. We can fast bail out. - completedWork.subtreeFlags |= subtreeFlags; - } else { - // Bubble up the earliest expiration time. - if ((completedWork.mode & ProfileMode) !== NoMode) { - // In profiling mode, resetChildExpirationTime is also used to reset - // profiler durations. - var _treeBaseDuration = completedWork.selfBaseDuration; - var _child2 = completedWork.child; + workInProgress.flags |= DidCapture; + } // If nothing suspended before and we're rendering the same children, + // then the tail doesn't matter. Anything new that suspends will work + // in the "together" mode, so we can continue from the state we had. - while (_child2 !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child2.lanes, _child2.childLanes) - ); // "Static" flags share the lifetime of the fiber/hook they belong to, - // so we should bubble those up even during a bailout. All the other - // flags have a lifetime only of a single render + commit, so we should - // ignore them. + var renderState = workInProgress.memoizedState; - subtreeFlags |= _child2.subtreeFlags & StaticMask; - subtreeFlags |= _child2.flags & StaticMask; - _treeBaseDuration += _child2.treeBaseDuration; - _child2 = _child2.sibling; - } + if (renderState !== null) { + // Reset to the "together" mode in case we've started a different + // update in the past but didn't complete it. + renderState.rendering = null; + renderState.tail = null; + renderState.lastEffect = null; + } - completedWork.treeBaseDuration = _treeBaseDuration; - } else { - var _child3 = completedWork.child; + pushSuspenseContext(workInProgress, suspenseStackCursor.current); - while (_child3 !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child3.lanes, _child3.childLanes) - ); // "Static" flags share the lifetime of the fiber/hook they belong to, - // so we should bubble those up even during a bailout. All the other - // flags have a lifetime only of a single render + commit, so we should - // ignore them. + if (_hasChildWork) { + break; + } else { + // If none of the children had any work, that means that none of + // them got retried so they'll still be blocked in the same way + // as before. We can fast bail out. + return null; + } + } - subtreeFlags |= _child3.subtreeFlags & StaticMask; - subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code - // smell because it assumes the commit phase is never concurrent with - // the render phase. Will address during refactor to alternate model. + case OffscreenComponent: + case LegacyHiddenComponent: { + // Need to check if the tree still needs to be deferred. This is + // almost identical to the logic used in the normal update path, + // so we'll just enter that. The only difference is we'll bail out + // at the next level instead of this one, because the child props + // have not changed. Which is fine. + // TODO: Probably should refactor `beginWork` to split the bailout + // path from the normal path. I'm tempted to do a labeled break here + // but I won't :) + workInProgress.lanes = NoLanes; + return updateOffscreenComponent(current, workInProgress, renderLanes); + } + } - _child3.return = completedWork; - _child3 = _child3.sibling; + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } else { + if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { + // This is a special case that only exists for legacy mode. + // See https://github.com/facebook/react/pull/19216. + didReceiveUpdate = true; + } else { + // An update was scheduled on this fiber, but there are no new props + // nor legacy context. Set this to false. If an update queue or context + // consumer produces a changed value, it will set this to true. Otherwise, + // the component will assume the children have not changed and bail out. + didReceiveUpdate = false; } } + } else { + didReceiveUpdate = false; + } // Before entering the begin phase, clear pending update priority. + // TODO: This assumes that we're about to evaluate the component and process + // the update queue. However, there's an exception: SimpleMemoComponent + // sometimes bails out later in the begin phase. This indicates that we should + // move this assignment out of the common path and into each branch. - completedWork.subtreeFlags |= subtreeFlags; - } + workInProgress.lanes = NoLanes; - completedWork.childLanes = newChildLanes; - return didBailout; -} + switch (workInProgress.tag) { + case IndeterminateComponent: { + return mountIndeterminateComponent( + current, + workInProgress, + workInProgress.type, + renderLanes + ); + } -function completeWork(current, workInProgress, renderLanes) { - var newProps = workInProgress.pendingProps; + case LazyComponent: { + var elementType = workInProgress.elementType; + return mountLazyComponent( + current, + workInProgress, + elementType, + updateLanes, + renderLanes + ); + } - switch (workInProgress.tag) { - case IndeterminateComponent: - case LazyComponent: - case SimpleMemoComponent: - case FunctionComponent: - case ForwardRef: - case Fragment: - case Mode: - case Profiler: - case ContextConsumer: - case MemoComponent: - bubbleProperties(workInProgress); - return null; + case FunctionComponent: { + var _Component = workInProgress.type; + var unresolvedProps = workInProgress.pendingProps; + var resolvedProps = + workInProgress.elementType === _Component + ? unresolvedProps + : resolveDefaultProps(_Component, unresolvedProps); + return updateFunctionComponent( + current, + workInProgress, + _Component, + resolvedProps, + renderLanes + ); + } case ClassComponent: { - var Component = workInProgress.type; + var _Component2 = workInProgress.type; + var _unresolvedProps = workInProgress.pendingProps; - if (isContextProvider(Component)) { - popContext(workInProgress); - } + var _resolvedProps = + workInProgress.elementType === _Component2 + ? _unresolvedProps + : resolveDefaultProps(_Component2, _unresolvedProps); - bubbleProperties(workInProgress); - return null; + return updateClassComponent( + current, + workInProgress, + _Component2, + _resolvedProps, + renderLanes + ); } - case HostRoot: { - var fiberRoot = workInProgress.stateNode; + case HostRoot: + return updateHostRoot(current, workInProgress, renderLanes); - popHostContainer(workInProgress); - popTopLevelContextObject(workInProgress); - resetWorkInProgressVersions(); + case HostComponent: + return updateHostComponent(current, workInProgress, renderLanes); - if (fiberRoot.pendingContext) { - fiberRoot.context = fiberRoot.pendingContext; - fiberRoot.pendingContext = null; - } + case HostText: + return updateHostText(); - if (current === null || current.child === null) { - // If we hydrated, pop so that we can delete any remaining children - // that weren't hydrated. - var wasHydrated = popHydrationState(); + case SuspenseComponent: + return updateSuspenseComponent(current, workInProgress, renderLanes); - if (wasHydrated) { - // If we hydrated, then we'll need to schedule an update for - // the commit side-effects on the root. - markUpdate(workInProgress); - } else if (!fiberRoot.hydrate) { - // Schedule an effect to clear this container at the start of the next commit. - // This handles the case of React rendering into a container with previous children. - // It's also safe to do for updates too, because current.child would only be null - // if the previous render was null (so the the container would already be empty). - workInProgress.flags |= Snapshot; - } - } + case HostPortal: + return updatePortalComponent(current, workInProgress, renderLanes); - updateHostContainer(current, workInProgress); - bubbleProperties(workInProgress); - return null; + case ForwardRef: { + var type = workInProgress.type; + var _unresolvedProps2 = workInProgress.pendingProps; + + var _resolvedProps2 = + workInProgress.elementType === type + ? _unresolvedProps2 + : resolveDefaultProps(type, _unresolvedProps2); + + return updateForwardRef( + current, + workInProgress, + type, + _resolvedProps2, + renderLanes + ); } - case HostComponent: { - popHostContext(workInProgress); - var rootContainerInstance = getRootHostContainer(); - var type = workInProgress.type; + case Fragment: + return updateFragment(current, workInProgress, renderLanes); - if (current !== null && workInProgress.stateNode != null) { - updateHostComponent$1( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ); + case Mode: + return updateMode(current, workInProgress, renderLanes); - if (current.ref !== workInProgress.ref) { - markRef$1(workInProgress); - } - } else { - if (!newProps) { - if (!(workInProgress.stateNode !== null)) { - throw Error( - "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." - ); - } // This can happen when we abort work. + case Profiler: + return updateProfiler(current, workInProgress, renderLanes); - bubbleProperties(workInProgress); - return null; - } + case ContextProvider: + return updateContextProvider(current, workInProgress, renderLanes); - var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context - // "stack" as the parent. Then append children as we go in beginWork - // or completeWork depending on whether we want to add them top->down or - // bottom->up. Top->down is faster in IE11. + case ContextConsumer: + return updateContextConsumer(current, workInProgress, renderLanes); - var _wasHydrated = popHydrationState(); + case MemoComponent: { + var _type2 = workInProgress.type; + var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. - if (_wasHydrated) { - // TODO: Move this and createInstance step into the beginPhase - // to consolidate. - if (prepareToHydrateHostInstance()) { - // If changes to the hydrated node need to be applied at the - // commit-phase we mark this as such. - markUpdate(workInProgress); - } - } else { - var instance = createInstance( - type, - newProps, - rootContainerInstance, - currentHostContext, - workInProgress - ); - appendAllChildren(instance, workInProgress, false, false); - workInProgress.stateNode = instance; // 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. + var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); - if (finalizeInitialChildren(instance)) { - markUpdate(workInProgress); - } - } + { + if (workInProgress.type !== workInProgress.elementType) { + var outerPropTypes = _type2.propTypes; - if (workInProgress.ref !== null) { - // If there is a ref on a host node we need to schedule a callback - markRef$1(workInProgress); + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + _resolvedProps3, // Resolved for outer only + "prop", + getComponentNameFromType(_type2) + ); + } } } - bubbleProperties(workInProgress); - return null; + _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); + return updateMemoComponent( + current, + workInProgress, + _type2, + _resolvedProps3, + updateLanes, + renderLanes + ); } - case HostText: { - var newText = newProps; + case SimpleMemoComponent: { + return updateSimpleMemoComponent( + current, + workInProgress, + workInProgress.type, + workInProgress.pendingProps, + updateLanes, + renderLanes + ); + } - if (current && workInProgress.stateNode != null) { - var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need - // to schedule a side-effect to do the updates. + case IncompleteClassComponent: { + var _Component3 = workInProgress.type; + var _unresolvedProps4 = workInProgress.pendingProps; - updateHostText$1(current, workInProgress, oldText, newText); - } else { - if (typeof newText !== "string") { - if (!(workInProgress.stateNode !== null)) { - throw Error( - "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." - ); - } // This can happen when we abort work. - } + var _resolvedProps4 = + workInProgress.elementType === _Component3 + ? _unresolvedProps4 + : resolveDefaultProps(_Component3, _unresolvedProps4); - var _rootContainerInstance = getRootHostContainer(); + return mountIncompleteClassComponent( + current, + workInProgress, + _Component3, + _resolvedProps4, + renderLanes + ); + } - var _currentHostContext = getHostContext(); + case SuspenseListComponent: { + return updateSuspenseListComponent(current, workInProgress, renderLanes); + } - var _wasHydrated2 = popHydrationState(); + case ScopeComponent: { + break; + } - if (_wasHydrated2) { - if (prepareToHydrateHostTextInstance()) { - markUpdate(workInProgress); - } - } else { - workInProgress.stateNode = createTextInstance( - newText, - _rootContainerInstance, - _currentHostContext, - workInProgress - ); - } - } + case OffscreenComponent: { + return updateOffscreenComponent(current, workInProgress, renderLanes); + } - bubbleProperties(workInProgress); - return null; + case LegacyHiddenComponent: { + return updateLegacyHiddenComponent(current, workInProgress, renderLanes); } + } - case SuspenseComponent: { - popSuspenseContext(workInProgress); - var nextState = workInProgress.memoizedState; + { + throw Error( + "Unknown unit of work tag (" + + workInProgress.tag + + "). This error is likely caused by a bug in React. Please file an issue." + ); + } +} - if ((workInProgress.flags & DidCapture) !== NoFlags) { - // Something suspended. Re-render with the fallback children. - workInProgress.lanes = renderLanes; // Do not reset the effect list. +function markUpdate(workInProgress) { + // Tag the fiber with an update effect. This turns a Placement into + // a PlacementAndUpdate. + workInProgress.flags |= Update; +} - if ((workInProgress.mode & ProfileMode) !== NoMode) { - transferActualDuration(workInProgress); - } // Don't bubble properties in this case. +function markRef$1(workInProgress) { + workInProgress.flags |= Ref; +} - return workInProgress; - } +var appendAllChildren; +var updateHostContainer; +var updateHostComponent$1; +var updateHostText$1; - var nextDidTimeout = nextState !== null; - var prevDidTimeout = false; +{ + // Mutation mode + appendAllChildren = function( + parent, + workInProgress, + needsVisibilityToggle, + isHidden + ) { + // We only have the top Fiber that was created but we need recurse down its + // children to find all the terminal nodes. + var node = workInProgress.child; - if (current === null) { - if (workInProgress.memoizedProps.fallback !== undefined); - } else { - var prevState = current.memoizedState; - prevDidTimeout = prevState !== null; + while (node !== null) { + if (node.tag === HostComponent || node.tag === HostText) { + appendInitialChild(parent, node.stateNode); + } else if (node.tag === HostPortal); + else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; } - if (nextDidTimeout && !prevDidTimeout) { - // TODO: This will still suspend a synchronous tree if anything - // in the concurrent tree already suspended during this render. - // This is a known bug. - if ((workInProgress.mode & ConcurrentMode) !== NoMode) { - // TODO: Move this back to throwException because this is too late - // if this is a large tree which is common for initial loads. We - // don't know if we should restart a render or not until we get - // this marker, and this is too late. - // If this render already had a ping or lower pri updates, - // and this is the first time we know we're going to suspend we - // should be able to immediately restart from within throwException. - var hasInvisibleChildContext = - current === null && - workInProgress.memoizedProps.unstable_avoidThisFallback !== true; - - if ( - hasInvisibleChildContext || - hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext - ) - ) { - // If this was in an invisible tree or a new render, then showing - // this boundary is ok. - renderDidSuspend(); - } else { - // Otherwise, we're going to have to hide content so we should - // suspend for longer if possible. - renderDidSuspendDelayIfPossible(); - } - } + if (node === workInProgress) { + return; } - { - // TODO: Only schedule updates if these values are non equal, i.e. it changed. - if (nextDidTimeout || prevDidTimeout) { - // If this boundary just timed out, schedule an effect to attach a - // retry listener to the promise. This flag is also used to hide the - // primary children. In mutation mode, we also need the flag to - // *unhide* children that were previously hidden, so check if this - // is currently timed out, too. - workInProgress.flags |= Update; + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; } - } - - bubbleProperties(workInProgress); - - { - if ((workInProgress.mode & ProfileMode) !== NoMode) { - if (nextDidTimeout) { - // Don't count time spent in a timed out Suspense subtree as part of the base duration. - var _primaryChildFragment2 = workInProgress.child; - if (_primaryChildFragment2 !== null) { - // $FlowFixMe Flow doesn't support type casting in combination with the -= operator - workInProgress.treeBaseDuration -= - _primaryChildFragment2.treeBaseDuration; - } - } - } + node = node.return; } - return null; + node.sibling.return = node.return; + node = node.sibling; } + }; - case HostPortal: - popHostContainer(workInProgress); - updateHostContainer(current, workInProgress); + updateHostContainer = function(current, workInProgress) { + // Noop + }; - if (current === null) { - preparePortalMount(workInProgress.stateNode.containerInfo); - } + updateHostComponent$1 = function( + current, + workInProgress, + type, + newProps, + rootContainerInstance + ) { + // If we have an alternate, that means this is an update and we need to + // schedule a side-effect to do the updates. + var oldProps = current.memoizedProps; - bubbleProperties(workInProgress); - return null; + if (oldProps === newProps) { + // In mutation mode, this is sufficient for a bailout because + // we won't touch this node even if children changed. + return; + } // If we get updated because one of our children updated, we don't + // have newProps so we'll have to reuse them. + // TODO: Split the update API as separate for the props vs. children. + // Even better would be if children weren't special cased at all tho. - case ContextProvider: - // Pop provider fiber - var context = workInProgress.type._context; - popProvider(context, workInProgress); - bubbleProperties(workInProgress); - return null; + 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`. - case IncompleteClassComponent: { - // Same as class component case. I put it down here so that the tags are - // sequential to ensure this switch is compiled to a jump table. - var _Component = workInProgress.type; + var updatePayload = prepareUpdate(); // TODO: Type this specific to this type of component. - if (isContextProvider(_Component)) { - popContext(workInProgress); - } + workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there + // is a new ref we mark this as an update. All the work is done in commitWork. - bubbleProperties(workInProgress); - return null; + if (updatePayload) { + markUpdate(workInProgress); } + }; - case SuspenseListComponent: { - popSuspenseContext(workInProgress); - var renderState = workInProgress.memoizedState; + updateHostText$1 = function(current, workInProgress, oldText, newText) { + // If the text differs, mark it as an update. All the work in done in commitWork. + if (oldText !== newText) { + markUpdate(workInProgress); + } + }; +} + +function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { + switch (renderState.tailMode) { + case "hidden": { + // Any insertions at the end of the tail list after this point + // should be invisible. If there are already mounted boundaries + // anything before them are not considered for collapsing. + // Therefore we need to go through the whole tail to find if + // there are any. + var tailNode = renderState.tail; + var lastTailNode = null; - if (renderState === null) { - // We're running in the default, "independent" mode. - // We don't do anything in this mode. - bubbleProperties(workInProgress); - return null; - } + while (tailNode !== null) { + if (tailNode.alternate !== null) { + lastTailNode = tailNode; + } - var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; - var renderedTail = renderState.rendering; + tailNode = tailNode.sibling; + } // Next we're simply going to delete all insertions after the + // last rendered item. - if (renderedTail === null) { - // We just rendered the head. - if (!didSuspendAlready) { - // This is the first pass. We need to figure out if anything is still - // suspended in the rendered set. - // If new content unsuspended, but there's still some content that - // didn't. Then we need to do a second pass that forces everything - // to keep showing their fallbacks. - // We might be suspended if something in this render pass suspended, or - // something in the previous committed pass suspended. Otherwise, - // there's no chance so we can skip the expensive call to - // findFirstSuspended. - var cannotBeSuspended = - renderHasNotSuspendedYet() && - (current === null || (current.flags & DidCapture) === NoFlags); + if (lastTailNode === null) { + // All remaining items in the tail are insertions. + renderState.tail = null; + } else { + // Detach the insertion after the last node that was already + // inserted. + lastTailNode.sibling = null; + } - if (!cannotBeSuspended) { - var row = workInProgress.child; + break; + } - while (row !== null) { - var suspended = findFirstSuspended(row); + case "collapsed": { + // Any insertions at the end of the tail list after this point + // should be invisible. If there are already mounted boundaries + // anything before them are not considered for collapsing. + // Therefore we need to go through the whole tail to find if + // there are any. + var _tailNode = renderState.tail; + var _lastTailNode = null; - if (suspended !== null) { - didSuspendAlready = true; - workInProgress.flags |= DidCapture; - cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as - // part of the second pass. In that case nothing will subscribe to - // its thennables. Instead, we'll transfer its thennables to the - // SuspenseList so that it can retry if they resolve. - // There might be multiple of these in the list but since we're - // going to wait for all of them anyway, it doesn't really matter - // which ones gets to ping. In theory we could get clever and keep - // track of how many dependencies remain but it gets tricky because - // in the meantime, we can add/remove/change items and dependencies. - // We might bail out of the loop before finding any but that - // doesn't matter since that means that the other boundaries that - // we did find already has their listeners attached. + while (_tailNode !== null) { + if (_tailNode.alternate !== null) { + _lastTailNode = _tailNode; + } - var newThennables = suspended.updateQueue; + _tailNode = _tailNode.sibling; + } // Next we're simply going to delete all insertions after the + // last rendered item. - if (newThennables !== null) { - workInProgress.updateQueue = newThennables; - workInProgress.flags |= Update; - } // Rerender the whole list, but this time, we'll force fallbacks - // to stay in place. - // Reset the effect flags before doing the second pass since that's now invalid. - // Reset the child fibers to their original state. + if (_lastTailNode === null) { + // All remaining items in the tail are insertions. + if (!hasRenderedATailFallback && renderState.tail !== null) { + // We suspended during the head. We want to show at least one + // row at the tail. So we'll keep on and cut off the rest. + renderState.tail.sibling = null; + } else { + renderState.tail = null; + } + } else { + // Detach the insertion after the last node that was already + // inserted. + _lastTailNode.sibling = null; + } - workInProgress.subtreeFlags = NoFlags; - resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately - // rerender the children. + break; + } + } +} - pushSuspenseContext( - workInProgress, - setShallowSuspenseContext( - suspenseStackCursor.current, - ForceSuspenseFallback - ) - ); // Don't bubble properties in this case. +function bubbleProperties(completedWork) { + var didBailout = + completedWork.alternate !== null && + completedWork.alternate.child === completedWork.child; + var newChildLanes = NoLanes; + var subtreeFlags = NoFlags; - return workInProgress.child; - } + if (!didBailout) { + // Bubble up the earliest expiration time. + if ((completedWork.mode & ProfileMode) !== NoMode) { + // In profiling mode, resetChildExpirationTime is also used to reset + // profiler durations. + var actualDuration = completedWork.actualDuration; + var treeBaseDuration = completedWork.selfBaseDuration; + var child = completedWork.child; - row = row.sibling; - } - } + while (child !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(child.lanes, child.childLanes) + ); + subtreeFlags |= child.subtreeFlags; + subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will + // only be updated if work is done on the fiber (i.e. it doesn't bailout). + // When work is done, it should bubble to the parent's actualDuration. If + // the fiber has not been cloned though, (meaning no work was done), then + // this value will reflect the amount of time spent working on a previous + // render. In that case it should not bubble. We determine whether it was + // cloned by comparing the child pointer. - if (renderState.tail !== null && now() > getRenderTargetTime()) { - // We have already passed our CPU deadline but we still have rows - // left in the tail. We'll just give up further attempts to render - // the main content and only render fallbacks. - workInProgress.flags |= DidCapture; - didSuspendAlready = true; - cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this - // to get it started back up to attempt the next item. While in terms - // of priority this work has the same priority as this current render, - // it's not part of the same transition once the transition has - // committed. If it's sync, we still want to yield so that it can be - // painted. Conceptually, this is really the same as pinging. - // We can use any RetryLane even if it's the one currently rendering - // since we're leaving it behind on this node. + actualDuration += child.actualDuration; + treeBaseDuration += child.treeBaseDuration; + child = child.sibling; + } - workInProgress.lanes = SomeRetryLane; - } - } else { - cutOffTailIfNeeded(renderState, false); - } // Next we're going to render the tail. - } else { - // Append the rendered row to the child list. - if (!didSuspendAlready) { - var _suspended = findFirstSuspended(renderedTail); + completedWork.actualDuration = actualDuration; + completedWork.treeBaseDuration = treeBaseDuration; + } else { + var _child = completedWork.child; - if (_suspended !== null) { - workInProgress.flags |= DidCapture; - didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't - // get lost if this row ends up dropped during a second pass. + while (_child !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child.lanes, _child.childLanes) + ); + subtreeFlags |= _child.subtreeFlags; + subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code + // smell because it assumes the commit phase is never concurrent with + // the render phase. Will address during refactor to alternate model. - var _newThennables = _suspended.updateQueue; + _child.return = completedWork; + _child = _child.sibling; + } + } - if (_newThennables !== null) { - workInProgress.updateQueue = _newThennables; - workInProgress.flags |= Update; - } + completedWork.subtreeFlags |= subtreeFlags; + } else { + // Bubble up the earliest expiration time. + if ((completedWork.mode & ProfileMode) !== NoMode) { + // In profiling mode, resetChildExpirationTime is also used to reset + // profiler durations. + var _treeBaseDuration = completedWork.selfBaseDuration; + var _child2 = completedWork.child; - cutOffTailIfNeeded(renderState, true); // This might have been modified. + while (_child2 !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child2.lanes, _child2.childLanes) + ); // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. - if ( - renderState.tail === null && - renderState.tailMode === "hidden" && - !renderedTail.alternate && - !getIsHydrating() // We don't cut it if we're hydrating. - ) { - // We're done. - bubbleProperties(workInProgress); - return null; - } - } else if ( - // The time it took to render last row is greater than the remaining - // time we have to render. So rendering one more row would likely - // exceed it. - now() * 2 - renderState.renderingStartTime > - getRenderTargetTime() && - renderLanes !== OffscreenLane - ) { - // We have now passed our CPU deadline and we'll just give up further - // attempts to render the main content and only render fallbacks. - // The assumption is that this is usually faster. - workInProgress.flags |= DidCapture; - didSuspendAlready = true; - cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this - // to get it started back up to attempt the next item. While in terms - // of priority this work has the same priority as this current render, - // it's not part of the same transition once the transition has - // committed. If it's sync, we still want to yield so that it can be - // painted. Conceptually, this is really the same as pinging. - // We can use any RetryLane even if it's the one currently rendering - // since we're leaving it behind on this node. + subtreeFlags |= _child2.subtreeFlags & StaticMask; + subtreeFlags |= _child2.flags & StaticMask; + _treeBaseDuration += _child2.treeBaseDuration; + _child2 = _child2.sibling; + } - workInProgress.lanes = SomeRetryLane; - } - } + completedWork.treeBaseDuration = _treeBaseDuration; + } else { + var _child3 = completedWork.child; - if (renderState.isBackwards) { - // The effect list of the backwards tail will have been added - // to the end. This breaks the guarantee that life-cycles fire in - // sibling order but that isn't a strong guarantee promised by React. - // Especially since these might also just pop in during future commits. - // Append to the beginning of the list. - renderedTail.sibling = workInProgress.child; - workInProgress.child = renderedTail; - } else { - var previousSibling = renderState.last; + while (_child3 !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child3.lanes, _child3.childLanes) + ); // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. - if (previousSibling !== null) { - previousSibling.sibling = renderedTail; - } else { - workInProgress.child = renderedTail; - } + subtreeFlags |= _child3.subtreeFlags & StaticMask; + subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code + // smell because it assumes the commit phase is never concurrent with + // the render phase. Will address during refactor to alternate model. - renderState.last = renderedTail; - } + _child3.return = completedWork; + _child3 = _child3.sibling; } + } - if (renderState.tail !== null) { - // We still have tail rows to render. - // Pop a row. - var next = renderState.tail; - renderState.rendering = next; - renderState.tail = next.sibling; - renderState.renderingStartTime = now(); - next.sibling = null; // Restore the context. - // TODO: We can probably just avoid popping it instead and only - // setting it the first time we go from not suspended to suspended. + completedWork.subtreeFlags |= subtreeFlags; + } - var suspenseContext = suspenseStackCursor.current; + completedWork.childLanes = newChildLanes; + return didBailout; +} - if (didSuspendAlready) { - suspenseContext = setShallowSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); - } else { - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - } +function completeWork(current, workInProgress, renderLanes) { + var newProps = workInProgress.pendingProps; - pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. - // Don't bubble properties in this case. + switch (workInProgress.tag) { + case IndeterminateComponent: + case LazyComponent: + case SimpleMemoComponent: + case FunctionComponent: + case ForwardRef: + case Fragment: + case Mode: + case Profiler: + case ContextConsumer: + case MemoComponent: + bubbleProperties(workInProgress); + return null; - return next; + case ClassComponent: { + var Component = workInProgress.type; + + if (isContextProvider(Component)) { + popContext(workInProgress); } bubbleProperties(workInProgress); return null; } - case ScopeComponent: { - break; - } + case HostRoot: { + var fiberRoot = workInProgress.stateNode; - case OffscreenComponent: - case LegacyHiddenComponent: { - popRenderLanes(workInProgress); - var _nextState = workInProgress.memoizedState; - var nextIsHidden = _nextState !== null; + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + resetWorkInProgressVersions(); - if (current !== null) { - var _prevState = current.memoizedState; - var prevIsHidden = _prevState !== null; + if (fiberRoot.pendingContext) { + fiberRoot.context = fiberRoot.pendingContext; + fiberRoot.pendingContext = null; + } - if ( - prevIsHidden !== nextIsHidden && - newProps.mode !== "unstable-defer-without-hiding" - ) { - workInProgress.flags |= Update; - } - } // Don't bubble properties for hidden children. + if (current === null || current.child === null) { + // If we hydrated, pop so that we can delete any remaining children + // that weren't hydrated. + var wasHydrated = popHydrationState(); - if ( - !nextIsHidden || - includesSomeLane(subtreeRenderLanes, OffscreenLane) || - (workInProgress.mode & ConcurrentMode) === NoMode - ) { - bubbleProperties(workInProgress); + if (wasHydrated) { + // If we hydrated, then we'll need to schedule an update for + // the commit side-effects on the root. + markUpdate(workInProgress); + } else if (!fiberRoot.hydrate) { + // Schedule an effect to clear this container at the start of the next commit. + // This handles the case of React rendering into a container with previous children. + // It's also safe to do for updates too, because current.child would only be null + // if the previous render was null (so the the container would already be empty). + workInProgress.flags |= Snapshot; + } } + updateHostContainer(current, workInProgress); + bubbleProperties(workInProgress); return null; } - } - { - throw Error( - "Unknown unit of work tag (" + - workInProgress.tag + - "). This error is likely caused by a bug in React. Please file an issue." - ); - } -} + case HostComponent: { + popHostContext(workInProgress); + var rootContainerInstance = getRootHostContainer(); + var type = workInProgress.type; -function unwindWork(workInProgress, renderLanes) { - switch (workInProgress.tag) { - case ClassComponent: { - var Component = workInProgress.type; + if (current !== null && workInProgress.stateNode != null) { + updateHostComponent$1( + current, + workInProgress, + type, + newProps, + rootContainerInstance + ); - if (isContextProvider(Component)) { - popContext(workInProgress); - } + if (current.ref !== workInProgress.ref) { + markRef$1(workInProgress); + } + } else { + if (!newProps) { + if (!(workInProgress.stateNode !== null)) { + throw Error( + "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." + ); + } // This can happen when we abort work. - var flags = workInProgress.flags; + bubbleProperties(workInProgress); + return null; + } - if (flags & ShouldCapture) { - workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; + var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context + // "stack" as the parent. Then append children as we go in beginWork + // or completeWork depending on whether we want to add them top->down or + // bottom->up. Top->down is faster in IE11. - if ((workInProgress.mode & ProfileMode) !== NoMode) { - transferActualDuration(workInProgress); + var _wasHydrated = popHydrationState(); + + if (_wasHydrated) { + // TODO: Move this and createInstance step into the beginPhase + // to consolidate. + if (prepareToHydrateHostInstance()) { + // If changes to the hydrated node need to be applied at the + // commit-phase we mark this as such. + markUpdate(workInProgress); + } + } else { + var instance = createInstance( + type, + newProps, + rootContainerInstance, + currentHostContext, + workInProgress + ); + appendAllChildren(instance, workInProgress, false, false); + workInProgress.stateNode = instance; // 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)) { + markUpdate(workInProgress); + } } - return workInProgress; + if (workInProgress.ref !== null) { + // If there is a ref on a host node we need to schedule a callback + markRef$1(workInProgress); + } } + bubbleProperties(workInProgress); return null; } - case HostRoot: { - popHostContainer(workInProgress); - popTopLevelContextObject(workInProgress); - resetWorkInProgressVersions(); - var _flags = workInProgress.flags; + case HostText: { + var newText = newProps; - if (!((_flags & DidCapture) === NoFlags)) { - throw Error( - "The root failed to unmount after an error. This is likely a bug in React. Please file an issue." - ); - } + if (current && workInProgress.stateNode != null) { + var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need + // to schedule a side-effect to do the updates. - workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture; - return workInProgress; - } + updateHostText$1(current, workInProgress, oldText, newText); + } else { + if (typeof newText !== "string") { + if (!(workInProgress.stateNode !== null)) { + throw Error( + "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." + ); + } // This can happen when we abort work. + } + + var _rootContainerInstance = getRootHostContainer(); + + var _currentHostContext = getHostContext(); + + var _wasHydrated2 = popHydrationState(); + + if (_wasHydrated2) { + if (prepareToHydrateHostTextInstance()) { + markUpdate(workInProgress); + } + } else { + workInProgress.stateNode = createTextInstance( + newText, + _rootContainerInstance, + _currentHostContext, + workInProgress + ); + } + } - case HostComponent: { - // TODO: popHydrationState - popHostContext(workInProgress); + bubbleProperties(workInProgress); return null; } case SuspenseComponent: { popSuspenseContext(workInProgress); + var nextState = workInProgress.memoizedState; - var _flags2 = workInProgress.flags; - - if (_flags2 & ShouldCapture) { - workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary. + if ((workInProgress.flags & DidCapture) !== NoFlags) { + // Something suspended. Re-render with the fallback children. + workInProgress.lanes = renderLanes; // Do not reset the effect list. if ((workInProgress.mode & ProfileMode) !== NoMode) { transferActualDuration(workInProgress); - } + } // Don't bubble properties in this case. return workInProgress; } - return null; - } + var nextDidTimeout = nextState !== null; + var prevDidTimeout = false; - case SuspenseListComponent: { - popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been - // caught by a nested boundary. If not, it should bubble through. + if (current === null) { + if (workInProgress.memoizedProps.fallback !== undefined); + } else { + var prevState = current.memoizedState; + prevDidTimeout = prevState !== null; + } + + if (nextDidTimeout && !prevDidTimeout) { + // TODO: This will still suspend a synchronous tree if anything + // in the concurrent tree already suspended during this render. + // This is a known bug. + if ((workInProgress.mode & ConcurrentMode) !== NoMode) { + // TODO: Move this back to throwException because this is too late + // if this is a large tree which is common for initial loads. We + // don't know if we should restart a render or not until we get + // this marker, and this is too late. + // If this render already had a ping or lower pri updates, + // and this is the first time we know we're going to suspend we + // should be able to immediately restart from within throwException. + var hasInvisibleChildContext = + current === null && + workInProgress.memoizedProps.unstable_avoidThisFallback !== true; + + if ( + hasInvisibleChildContext || + hasSuspenseContext( + suspenseStackCursor.current, + InvisibleParentSuspenseContext + ) + ) { + // If this was in an invisible tree or a new render, then showing + // this boundary is ok. + renderDidSuspend(); + } else { + // Otherwise, we're going to have to hide content so we should + // suspend for longer if possible. + renderDidSuspendDelayIfPossible(); + } + } + } + + { + // TODO: Only schedule updates if these values are non equal, i.e. it changed. + if (nextDidTimeout || prevDidTimeout) { + // If this boundary just timed out, schedule an effect to attach a + // retry listener to the promise. This flag is also used to hide the + // primary children. In mutation mode, we also need the flag to + // *unhide* children that were previously hidden, so check if this + // is currently timed out, too. + workInProgress.flags |= Update; + } + } + + bubbleProperties(workInProgress); + + { + if ((workInProgress.mode & ProfileMode) !== NoMode) { + if (nextDidTimeout) { + // Don't count time spent in a timed out Suspense subtree as part of the base duration. + var _primaryChildFragment2 = workInProgress.child; + + if (_primaryChildFragment2 !== null) { + // $FlowFixMe Flow doesn't support type casting in combination with the -= operator + workInProgress.treeBaseDuration -= + _primaryChildFragment2.treeBaseDuration; + } + } + } + } return null; } case HostPortal: popHostContainer(workInProgress); + updateHostContainer(current, workInProgress); + + if (current === null) { + preparePortalMount(workInProgress.stateNode.containerInfo); + } + + bubbleProperties(workInProgress); return null; case ContextProvider: + // Pop provider fiber var context = workInProgress.type._context; popProvider(context, workInProgress); + bubbleProperties(workInProgress); return null; - case OffscreenComponent: - case LegacyHiddenComponent: - popRenderLanes(workInProgress); - - return null; - - case CacheComponent: - return null; - - default: - return null; - } -} - -function unwindInterruptedWork(interruptedWork, renderLanes) { - switch (interruptedWork.tag) { - case ClassComponent: { - var childContextTypes = interruptedWork.type.childContextTypes; + case IncompleteClassComponent: { + // Same as class component case. I put it down here so that the tags are + // sequential to ensure this switch is compiled to a jump table. + var _Component = workInProgress.type; - if (childContextTypes !== null && childContextTypes !== undefined) { - popContext(interruptedWork); + if (isContextProvider(_Component)) { + popContext(workInProgress); } - break; - } - - case HostRoot: { - popHostContainer(interruptedWork); - popTopLevelContextObject(interruptedWork); - resetWorkInProgressVersions(); - break; - } - - case HostComponent: { - popHostContext(interruptedWork); - break; + bubbleProperties(workInProgress); + return null; } - case HostPortal: - popHostContainer(interruptedWork); - break; - - case SuspenseComponent: - popSuspenseContext(interruptedWork); - break; - - case SuspenseListComponent: - popSuspenseContext(interruptedWork); - break; + case SuspenseListComponent: { + popSuspenseContext(workInProgress); + var renderState = workInProgress.memoizedState; - case ContextProvider: - var context = interruptedWork.type._context; - popProvider(context, interruptedWork); - break; + if (renderState === null) { + // We're running in the default, "independent" mode. + // We don't do anything in this mode. + bubbleProperties(workInProgress); + return null; + } - case OffscreenComponent: - case LegacyHiddenComponent: - popRenderLanes(interruptedWork); + var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; + var renderedTail = renderState.rendering; - break; - } -} + if (renderedTail === null) { + // We just rendered the head. + if (!didSuspendAlready) { + // This is the first pass. We need to figure out if anything is still + // suspended in the rendered set. + // If new content unsuspended, but there's still some content that + // didn't. Then we need to do a second pass that forces everything + // to keep showing their fallbacks. + // We might be suspended if something in this render pass suspended, or + // something in the previous committed pass suspended. Otherwise, + // there's no chance so we can skip the expensive call to + // findFirstSuspended. + var cannotBeSuspended = + renderHasNotSuspendedYet() && + (current === null || (current.flags & DidCapture) === NoFlags); -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: getStackByFiberInDevAndProd(source) - }; -} + if (!cannotBeSuspended) { + var row = workInProgress.child; -if ( - !( - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog === - "function" - ) -) { - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -} + while (row !== null) { + var suspended = findFirstSuspended(row); -function showErrorDialog(boundary, errorInfo) { - var capturedError = { - componentStack: errorInfo.stack !== null ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - boundary !== null && boundary.tag === ClassComponent - ? boundary.stateNode - : null - }; - return ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog( - capturedError - ); -} + if (suspended !== null) { + didSuspendAlready = true; + workInProgress.flags |= DidCapture; + cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as + // part of the second pass. In that case nothing will subscribe to + // its thennables. Instead, we'll transfer its thennables to the + // SuspenseList so that it can retry if they resolve. + // There might be multiple of these in the list but since we're + // going to wait for all of them anyway, it doesn't really matter + // which ones gets to ping. In theory we could get clever and keep + // track of how many dependencies remain but it gets tricky because + // in the meantime, we can add/remove/change items and dependencies. + // We might bail out of the loop before finding any but that + // doesn't matter since that means that the other boundaries that + // we did find already has their listeners attached. -function logCapturedError(boundary, errorInfo) { - try { - var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. - // This enables renderers like ReactNative to better manage redbox behavior. + var newThennables = suspended.updateQueue; - if (logError === false) { - return; - } + if (newThennables !== null) { + workInProgress.updateQueue = newThennables; + workInProgress.flags |= Update; + } // Rerender the whole list, but this time, we'll force fallbacks + // to stay in place. + // Reset the effect flags before doing the second pass since that's now invalid. + // Reset the child fibers to their original state. - var error = errorInfo.value; + workInProgress.subtreeFlags = NoFlags; + resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately + // rerender the children. - if (true) { - var source = errorInfo.source; - var stack = errorInfo.stack; - var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling - // `preventDefault()` in window `error` handler. - // We record this information as an expando on the error. + pushSuspenseContext( + workInProgress, + setShallowSuspenseContext( + suspenseStackCursor.current, + ForceSuspenseFallback + ) + ); // Don't bubble properties in this case. - if (error != null && error._suppressLogging) { - if (boundary.tag === ClassComponent) { - // The error is recoverable and was silenced. - // Ignore it and don't print the stack addendum. - // This is handy for testing error boundaries without noise. - return; - } // The error is fatal. Since the silencing might have - // been accidental, we'll surface it anyway. - // However, the browser would have silenced the original error - // so we'll print it first, and then print the stack addendum. + return workInProgress.child; + } - console["error"](error); // Don't transform to our wrapper - // For a more detailed description of this block, see: - // https://github.com/facebook/react/pull/13384 - } + row = row.sibling; + } + } - var componentName = source ? getComponentNameFromFiber(source) : null; - var componentNameMessage = componentName - ? "The above error occurred in the <" + componentName + "> component:" - : "The above error occurred in one of your React components:"; - var errorBoundaryMessage; + if (renderState.tail !== null && now() > getRenderTargetTime()) { + // We have already passed our CPU deadline but we still have rows + // left in the tail. We'll just give up further attempts to render + // the main content and only render fallbacks. + workInProgress.flags |= DidCapture; + didSuspendAlready = true; + cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this + // to get it started back up to attempt the next item. While in terms + // of priority this work has the same priority as this current render, + // it's not part of the same transition once the transition has + // committed. If it's sync, we still want to yield so that it can be + // painted. Conceptually, this is really the same as pinging. + // We can use any RetryLane even if it's the one currently rendering + // since we're leaving it behind on this node. - if (boundary.tag === HostRoot) { - errorBoundaryMessage = - "Consider adding an error boundary to your tree to customize error handling behavior.\n" + - "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries."; + workInProgress.lanes = SomeRetryLane; + } + } else { + cutOffTailIfNeeded(renderState, false); + } // Next we're going to render the tail. } else { - var errorBoundaryName = - getComponentNameFromFiber(boundary) || "Anonymous"; - errorBoundaryMessage = - "React will try to recreate this component tree from scratch " + - ("using the error boundary you provided, " + errorBoundaryName + "."); - } - - var combinedMessage = - componentNameMessage + - "\n" + - 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. + // Append the rendered row to the child list. + if (!didSuspendAlready) { + var _suspended = findFirstSuspended(renderedTail); - console["error"](combinedMessage); // Don't transform to our wrapper - } else { - // In production, we print the error directly. - // This will include the message, the JS stack, and anything the browser wants to show. - // We pass the error object instead of custom message so that the browser displays the error natively. - console["error"](error); // Don't transform to our wrapper - } - } catch (e) { - // This method must not throw, or React internal state will get messed up. - // If console.error is overridden, or logCapturedError() shows a dialog that throws, - // we want to report this error outside of the normal stack as a last resort. - // https://github.com/facebook/react/issues/13188 - setTimeout(function() { - throw e; - }); - } -} + if (_suspended !== null) { + workInProgress.flags |= DidCapture; + didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't + // get lost if this row ends up dropped during a second pass. -var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; + var _newThennables = _suspended.updateQueue; -function createRootErrorUpdate(fiber, errorInfo, lane) { - var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. + if (_newThennables !== null) { + workInProgress.updateQueue = _newThennables; + workInProgress.flags |= Update; + } - update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property - // being called "element". + cutOffTailIfNeeded(renderState, true); // This might have been modified. - update.payload = { - element: null - }; - var error = errorInfo.value; + if ( + renderState.tail === null && + renderState.tailMode === "hidden" && + !renderedTail.alternate && + !getIsHydrating() // We don't cut it if we're hydrating. + ) { + // We're done. + bubbleProperties(workInProgress); + return null; + } + } else if ( + // The time it took to render last row is greater than the remaining + // time we have to render. So rendering one more row would likely + // exceed it. + now() * 2 - renderState.renderingStartTime > + getRenderTargetTime() && + renderLanes !== OffscreenLane + ) { + // We have now passed our CPU deadline and we'll just give up further + // attempts to render the main content and only render fallbacks. + // The assumption is that this is usually faster. + workInProgress.flags |= DidCapture; + didSuspendAlready = true; + cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this + // to get it started back up to attempt the next item. While in terms + // of priority this work has the same priority as this current render, + // it's not part of the same transition once the transition has + // committed. If it's sync, we still want to yield so that it can be + // painted. Conceptually, this is really the same as pinging. + // We can use any RetryLane even if it's the one currently rendering + // since we're leaving it behind on this node. - update.callback = function() { - onUncaughtError(error); - logCapturedError(fiber, errorInfo); - }; + workInProgress.lanes = SomeRetryLane; + } + } - return update; -} + if (renderState.isBackwards) { + // The effect list of the backwards tail will have been added + // to the end. This breaks the guarantee that life-cycles fire in + // sibling order but that isn't a strong guarantee promised by React. + // Especially since these might also just pop in during future commits. + // Append to the beginning of the list. + renderedTail.sibling = workInProgress.child; + workInProgress.child = renderedTail; + } else { + var previousSibling = renderState.last; -function createClassErrorUpdate(fiber, errorInfo, lane) { - var update = createUpdate(NoTimestamp, lane); - update.tag = CaptureUpdate; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if (previousSibling !== null) { + previousSibling.sibling = renderedTail; + } else { + workInProgress.child = renderedTail; + } - if (typeof getDerivedStateFromError === "function") { - var error$1 = errorInfo.value; + renderState.last = renderedTail; + } + } - update.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error$1); - }; - } + if (renderState.tail !== null) { + // We still have tail rows to render. + // Pop a row. + var next = renderState.tail; + renderState.rendering = next; + renderState.tail = next.sibling; + renderState.renderingStartTime = now(); + next.sibling = null; // Restore the context. + // TODO: We can probably just avoid popping it instead and only + // setting it the first time we go from not suspended to suspended. - var inst = fiber.stateNode; + var suspenseContext = suspenseStackCursor.current; - if (inst !== null && typeof inst.componentDidCatch === "function") { - update.callback = function callback() { - { - markFailedErrorBoundaryForHotReloading(fiber); - } + if (didSuspendAlready) { + suspenseContext = setShallowSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + } else { + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + } - if (typeof getDerivedStateFromError !== "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 getDerivedStateFromError is - // not defined. - markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined + pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. + // Don't bubble properties in this case. - logCapturedError(fiber, errorInfo); + return next; } - var error$1 = errorInfo.value; - var stack = errorInfo.stack; - this.componentDidCatch(error$1, { - componentStack: stack !== null ? stack : "" - }); + bubbleProperties(workInProgress); + return null; + } - { - if (typeof getDerivedStateFromError !== "function") { - // If componentDidCatch is the only error boundary method defined, - // then it needs to call setState to recover from errors. - // If no state update is scheduled then the boundary will swallow the error. - if (!includesSomeLane(fiber.lanes, SyncLane)) { - error( - "%s: Error boundaries should implement getDerivedStateFromError(). " + - "In that method, return a state update to display an error message or fallback UI.", - getComponentNameFromFiber(fiber) || "Unknown" - ); - } - } - } - }; - } else { - update.callback = function() { - markFailedErrorBoundaryForHotReloading(fiber); - }; - } + case ScopeComponent: { + break; + } + + case OffscreenComponent: + case LegacyHiddenComponent: { + popRenderLanes(workInProgress); + var _nextState = workInProgress.memoizedState; + var nextIsHidden = _nextState !== null; - return update; -} + if (current !== null) { + var _prevState = current.memoizedState; + var prevIsHidden = _prevState !== null; -function attachPingListener(root, wakeable, lanes) { - // Attach a listener to the promise to "ping" the root and retry. But only if - // one does not already exist for the lanes we're currently rendering (which - // acts like a "thread ID" here). - var pingCache = root.pingCache; - var threadIDs; + if ( + prevIsHidden !== nextIsHidden && + newProps.mode !== "unstable-defer-without-hiding" + ) { + workInProgress.flags |= Update; + } + } // Don't bubble properties for hidden children. - if (pingCache === null) { - pingCache = root.pingCache = new PossiblyWeakMap$1(); - threadIDs = new Set(); - pingCache.set(wakeable, threadIDs); - } else { - threadIDs = pingCache.get(wakeable); + if ( + !nextIsHidden || + includesSomeLane(subtreeRenderLanes, OffscreenLane) || + (workInProgress.mode & ConcurrentMode) === NoMode + ) { + bubbleProperties(workInProgress); + } - if (threadIDs === undefined) { - threadIDs = new Set(); - pingCache.set(wakeable, threadIDs); + return null; } } - if (!threadIDs.has(lanes)) { - // Memoize using the thread ID to prevent redundant listeners. - threadIDs.add(lanes); - var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); - - wakeable.then(ping, ping); + { + throw Error( + "Unknown unit of work tag (" + + workInProgress.tag + + "). This error is likely caused by a bug in React. Please file an issue." + ); } } -function throwException( - root, - returnFiber, - sourceFiber, - value, - rootRenderLanes -) { - // The source fiber did not complete. - sourceFiber.flags |= Incomplete; +function unwindWork(workInProgress, renderLanes) { + switch (workInProgress.tag) { + case ClassComponent: { + var Component = workInProgress.type; - if ( - value !== null && - typeof value === "object" && - typeof value.then === "function" - ) { - var wakeable = value; - // A legacy mode Suspense quirk, only relevant to hook components. + if (isContextProvider(Component)) { + popContext(workInProgress); + } - var tag = sourceFiber.tag; + var flags = workInProgress.flags; - if ( - (sourceFiber.mode & ConcurrentMode) === NoMode && - (tag === FunctionComponent || - tag === ForwardRef || - tag === SimpleMemoComponent) - ) { - var currentSource = sourceFiber.alternate; + if (flags & ShouldCapture) { + workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; - if (currentSource) { - sourceFiber.updateQueue = currentSource.updateQueue; - sourceFiber.memoizedState = currentSource.memoizedState; - sourceFiber.lanes = currentSource.lanes; - } else { - sourceFiber.updateQueue = null; - sourceFiber.memoizedState = null; + if ((workInProgress.mode & ProfileMode) !== NoMode) { + transferActualDuration(workInProgress); + } + + return workInProgress; } + + return null; } - var hasInvisibleParentBoundary = hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext - ); // Schedule the nearest Suspense to re-render the timed out view. + case HostRoot: { + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + resetWorkInProgressVersions(); + var _flags = workInProgress.flags; - var _workInProgress = returnFiber; + if (!((_flags & DidCapture) === NoFlags)) { + throw Error( + "The root failed to unmount after an error. This is likely a bug in React. Please file an issue." + ); + } - do { - if ( - _workInProgress.tag === SuspenseComponent && - shouldCaptureSuspense(_workInProgress, hasInvisibleParentBoundary) - ) { - // Found the nearest boundary. - // Stash the promise on the boundary fiber. If the boundary times out, we'll - // attach another listener to flip the boundary back to its normal state. - var wakeables = _workInProgress.updateQueue; + workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture; + return workInProgress; + } - if (wakeables === null) { - var updateQueue = new Set(); - updateQueue.add(wakeable); - _workInProgress.updateQueue = updateQueue; - } else { - wakeables.add(wakeable); - } // If the boundary is in legacy mode, we should *not* - // suspend the commit. Pretend as if the suspended component rendered - // null and keep rendering. In the commit phase, we'll schedule a - // subsequent synchronous update to re-render the Suspense. - // - // Note: It doesn't matter whether the component that suspended was - // inside a concurrent mode tree. If the Suspense is outside of it, we - // should *not* suspend the commit. - // - // If the suspense boundary suspended itself suspended, we don't have to - // do this trick because nothing was partially started. We can just - // directly do a second pass over the fallback in this render and - // pretend we meant to render that directly. + case HostComponent: { + // TODO: popHydrationState + popHostContext(workInProgress); + return null; + } - if ( - (_workInProgress.mode & ConcurrentMode) === NoMode && - _workInProgress !== returnFiber - ) { - _workInProgress.flags |= DidCapture; - sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. - // But we shouldn't call any lifecycle methods or callbacks. Remove - // all lifecycle effect tags. + case SuspenseComponent: { + popSuspenseContext(workInProgress); - sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); + var _flags2 = workInProgress.flags; - if (sourceFiber.tag === ClassComponent) { - var _currentSourceFiber = sourceFiber.alternate; + if (_flags2 & ShouldCapture) { + workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary. - if (_currentSourceFiber === null) { - // This is a new mount. Change the tag so it's not mistaken for a - // completed class component. For example, we should not call - // componentWillUnmount if it is deleted. - sourceFiber.tag = IncompleteClassComponent; - } else { - // When we try rendering again, we should not reuse the current fiber, - // since it's known to be in an inconsistent state. Use a force update to - // prevent a bail out. - var update = createUpdate(NoTimestamp, SyncLane); - update.tag = ForceUpdate; - enqueueUpdate(sourceFiber, update); - } - } // The source fiber did not complete. Mark it with Sync priority to - // indicate that it still has pending work. + if ((workInProgress.mode & ProfileMode) !== NoMode) { + transferActualDuration(workInProgress); + } - sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); // Exit without suspending. + return workInProgress; + } - return; - } // Confirmed that the boundary is in a concurrent mode tree. Continue - // with the normal suspend path. - // - // After this we'll use a set of heuristics to determine whether this - // render pass will run to completion or restart or "suspend" the commit. - // The actual logic for this is spread out in different places. - // - // This first principle is that if we're going to suspend when we complete - // a root, then we should also restart if we get an update or ping that - // might unsuspend it, and vice versa. The only reason to suspend is - // because you think you might want to restart before committing. However, - // it doesn't make sense to restart only while in the period we're suspended. - // - // Restarting too aggressively is also not good because it starves out any - // intermediate loading state. So we use heuristics to determine when. - // Suspense Heuristics - // - // If nothing threw a Promise or all the same fallbacks are already showing, - // then don't suspend/restart. - // - // If this is an initial render of a new tree of Suspense boundaries and - // those trigger a fallback, then don't suspend/restart. We want to ensure - // that we can show the initial loading state as quickly as possible. - // - // If we hit a "Delayed" case, such as when we'd switch from content back into - // a fallback, then we should always suspend/restart. Transitions apply - // to this case. If none is defined, JND is used instead. - // - // If we're already showing a fallback and it gets "retried", allowing us to show - // another level, but there's still an inner boundary that would show a fallback, - // then we suspend/restart for 500ms since the last time we showed a fallback - // anywhere in the tree. This effectively throttles progressive loading into a - // consistent train of commits. This also gives us an opportunity to restart to - // get to the completed state slightly earlier. - // - // If there's ambiguity due to batching it's resolved in preference of: - // 1) "delayed", 2) "initial render", 3) "retry". - // - // We want to ensure that a "busy" state doesn't get force committed. We want to - // ensure that new initial loading states can commit as soon as possible. + return null; + } - attachPingListener(root, wakeable, rootRenderLanes); - _workInProgress.flags |= ShouldCapture; - _workInProgress.lanes = rootRenderLanes; - return; - } // This boundary already captured during this render. Continue to the next - // boundary. + case SuspenseListComponent: { + popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been + // caught by a nested boundary. If not, it should bubble through. - _workInProgress = _workInProgress.return; - } while (_workInProgress !== null); // No boundary was found. Fallthrough to error mode. - // TODO: Use invariant so the message is stripped in prod? + return null; + } + + case HostPortal: + popHostContainer(workInProgress); + return null; + + case ContextProvider: + var context = workInProgress.type._context; + popProvider(context, workInProgress); + return null; + + case OffscreenComponent: + case LegacyHiddenComponent: + popRenderLanes(workInProgress); - value = new Error( - (getComponentNameFromFiber(sourceFiber) || "A React component") + - " suspended while rendering, but no fallback UI was specified.\n" + - "\n" + - "Add a component higher in the tree to " + - "provide a loading indicator or placeholder to display." - ); - } // We didn't find a boundary that could handle this type of exception. Start - // over and traverse parent path again, this time treating the exception - // as an error. + return null; - renderDidError(); - value = createCapturedValue(value, sourceFiber); - var workInProgress = returnFiber; + case CacheComponent: + return null; - do { - switch (workInProgress.tag) { - case HostRoot: { - var _errorInfo = value; - workInProgress.flags |= ShouldCapture; - var lane = pickArbitraryLane(rootRenderLanes); - workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); + default: + return null; + } +} - var _update = createRootErrorUpdate(workInProgress, _errorInfo, lane); +function unwindInterruptedWork(interruptedWork, renderLanes) { + switch (interruptedWork.tag) { + case ClassComponent: { + var childContextTypes = interruptedWork.type.childContextTypes; - enqueueCapturedUpdate(workInProgress, _update); - return; + if (childContextTypes !== null && childContextTypes !== undefined) { + popContext(interruptedWork); } - case ClassComponent: - // Capture and retry - var errorInfo = value; - var ctor = workInProgress.type; - var instance = workInProgress.stateNode; + break; + } - if ( - (workInProgress.flags & DidCapture) === NoFlags && - (typeof ctor.getDerivedStateFromError === "function" || - (instance !== null && - typeof instance.componentDidCatch === "function" && - !isAlreadyFailedLegacyErrorBoundary(instance))) - ) { - workInProgress.flags |= ShouldCapture; + case HostRoot: { + popHostContainer(interruptedWork); + popTopLevelContextObject(interruptedWork); + resetWorkInProgressVersions(); + break; + } - var _lane = pickArbitraryLane(rootRenderLanes); + case HostComponent: { + popHostContext(interruptedWork); + break; + } - workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state + case HostPortal: + popHostContainer(interruptedWork); + break; - var _update2 = createClassErrorUpdate( - workInProgress, - errorInfo, - _lane - ); + case SuspenseComponent: + popSuspenseContext(interruptedWork); + break; - enqueueCapturedUpdate(workInProgress, _update2); - return; - } + case SuspenseListComponent: + popSuspenseContext(interruptedWork); + break; - break; - } + case ContextProvider: + var context = interruptedWork.type._context; + popProvider(context, interruptedWork); + break; - workInProgress = workInProgress.return; - } while (workInProgress !== null); + case OffscreenComponent: + case LegacyHiddenComponent: + popRenderLanes(interruptedWork); + + break; + } } var didWarnAboutUndefinedSnapshotBeforeUpdate = null; @@ -16846,11 +17062,21 @@ var didWarnAboutUndefinedSnapshotBeforeUpdate = null; var PossiblyWeakSet = typeof WeakSet === "function" ? WeakSet : Set; var nextEffect = null; // Used for Profiling builds to track updaters. +var inProgressLanes = null; +var inProgressRoot = null; + var callComponentWillUnmountWithTimer = function(current, instance) { instance.props = current.memoizedProps; instance.state = current.memoizedState; - { + if (current.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentWillUnmount(); + } finally { + recordLayoutEffectDuration(current); + } + } else { instance.componentWillUnmount(); } }; // Capture errors so they don't interrupt mounting. @@ -16882,7 +17108,11 @@ function safelyDetachRef(current, nearestMountedAncestor) { if (ref !== null) { if (typeof ref === "function") { { - { + if (current.mode & ProfileMode) { + startLayoutEffectTimer(); + invokeGuardedCallback(null, ref, null, null); + recordLayoutEffectDuration(current); + } else { invokeGuardedCallback(null, ref, null, null); } @@ -17194,6 +17424,58 @@ function commitHookEffectListMount(tag, finishedWork) { } } +function commitPassiveEffectDurations(finishedRoot, finishedWork) { + { + // Only Profilers with work in their subtree will have an Update effect scheduled. + if ((finishedWork.flags & Update) !== NoFlags) { + switch (finishedWork.tag) { + case Profiler: { + var passiveEffectDuration = + finishedWork.stateNode.passiveEffectDuration; + var _finishedWork$memoize = finishedWork.memoizedProps, + id = _finishedWork$memoize.id, + onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase. + // It does not get reset until the start of the next commit phase. + + var commitTime = getCommitTime(); + var phase = finishedWork.alternate === null ? "mount" : "update"; + + { + if (isCurrentUpdateNested()) { + phase = "nested-update"; + } + } + + if (typeof onPostCommit === "function") { + onPostCommit(id, phase, passiveEffectDuration, commitTime); + } // Bubble times to the next nearest ancestor Profiler. + // After we process that Profiler, we'll bubble further up. + + var parentFiber = finishedWork.return; + + outer: while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.passiveEffectDuration += passiveEffectDuration; + break outer; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.passiveEffectDuration += passiveEffectDuration; + break outer; + } + + parentFiber = parentFiber.return; + } + + break; + } + } + } + } +} + function commitLayoutEffectOnFiber( finishedRoot, current, @@ -17209,7 +17491,14 @@ function commitLayoutEffectOnFiber( // This is done to prevent sibling component effects from interfering with each other, // e.g. a destroy function in one component should never override a ref set // by a create function in another component during the same commit. - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + commitHookEffectListMount(Layout | HasEffect, finishedWork); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { commitHookEffectListMount(Layout | HasEffect, finishedWork); } @@ -17253,7 +17542,14 @@ function commitLayoutEffectOnFiber( } } - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentDidMount(); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { instance.componentDidMount(); } } else { @@ -17294,7 +17590,18 @@ function commitLayoutEffectOnFiber( } } - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate + ); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { instance.componentDidUpdate( prevProps, prevState, @@ -17404,6 +17711,12 @@ function commitLayoutEffectOnFiber( var commitTime = getCommitTime(); var phase = current === null ? "mount" : "update"; + { + if (isCurrentUpdateNested()) { + phase = "nested-update"; + } + } + if (typeof onRender === "function") { onRender( finishedWork.memoizedProps.id, @@ -17414,6 +17727,40 @@ function commitLayoutEffectOnFiber( commitTime ); } + + { + if (typeof onCommit === "function") { + onCommit( + finishedWork.memoizedProps.id, + phase, + effectDuration, + commitTime + ); + } // Schedule a passive effect for this Profiler to call onPostCommit hooks. + // This effect should be scheduled even if there is no onPostCommit callback for this Profiler, + // because the effect is also where times bubble to parent Profilers. + + enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor. + // Do not reset these values until the next render so DevTools has a chance to read them first. + + var parentFiber = finishedWork.return; + + outer: while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.effectDuration += effectDuration; + break outer; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.effectDuration += effectDuration; + break outer; + } + + parentFiber = parentFiber.return; + } + } } break; @@ -17535,7 +17882,14 @@ function commitAttachRef(finishedWork) { } // Moved outside to ensure DCE works with this flag if (typeof ref === "function") { - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + ref(instanceToUse); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { ref(instanceToUse); } } else { @@ -17559,7 +17913,14 @@ function commitDetachRef(current) { if (currentRef !== null) { if (typeof currentRef === "function") { - { + if (current.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + currentRef(null); + } finally { + recordLayoutEffectDuration(current); + } + } else { currentRef(null); } } else { @@ -17594,7 +17955,11 @@ function commitUnmount(finishedRoot, current, nearestMountedAncestor) { if (destroy !== undefined) { if ((tag & Layout) !== NoFlags$1) { - { + if (current.mode & ProfileMode) { + startLayoutEffectTimer(); + safelyCallDestroy(current, nearestMountedAncestor, destroy); + recordLayoutEffectDuration(current); + } else { safelyCallDestroy(current, nearestMountedAncestor, destroy); } } @@ -18039,7 +18404,18 @@ function commitWork(current, finishedWork) { // This prevents sibling component effects from interfering with each other, // e.g. a destroy function in one component should never override a ref set // by a create function in another component during the same commit. - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + commitHookEffectListUnmount( + Layout | HasEffect, + finishedWork, + finishedWork.return + ); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { commitHookEffectListUnmount( Layout | HasEffect, finishedWork, @@ -18180,6 +18556,19 @@ function attachSuspenseRetryListeners(finishedWork) { if (!retryCache.has(wakeable)) { retryCache.add(wakeable); + { + if (isDevToolsPresent) { + if (inProgressLanes !== null && inProgressRoot !== null) { + // If we have pending work still, associate the original updaters with it. + restorePendingUpdaters(inProgressRoot, inProgressLanes); + } else { + throw Error( + "Expected finished root and lanes to be set. This is a bug in React." + ); + } + } + } + wakeable.then(retry, retry); } }); @@ -18206,8 +18595,12 @@ function commitResetTextContent(current) { } function commitMutationEffects(root, firstChild, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; nextEffect = firstChild; commitMutationEffects_begin(root); + inProgressLanes = null; + inProgressRoot = null; } function commitMutationEffects_begin(root) { @@ -18348,8 +18741,12 @@ function commitMutationEffectsOnFiber(finishedWork, root) { } function commitLayoutEffects(finishedWork, root, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; nextEffect = finishedWork; commitLayoutEffects_begin(finishedWork, root, committedLanes); + inProgressLanes = null; + inProgressRoot = null; } function commitLayoutEffects_begin(subtreeRoot, root, committedLanes) { @@ -18482,7 +18879,15 @@ function commitPassiveMountOnFiber(finishedRoot, finishedWork) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (finishedWork.mode & ProfileMode) { + startPassiveEffectTimer(); + + try { + commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); + } finally { + recordPassiveEffectDuration(finishedWork); + } + } else { commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); } @@ -18583,7 +18988,15 @@ function commitPassiveUnmountOnFiber(finishedWork) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (finishedWork.mode & ProfileMode) { + startPassiveEffectTimer(); + commitHookEffectListUnmount( + Passive$1 | HasEffect, + finishedWork, + finishedWork.return + ); + recordPassiveEffectDuration(finishedWork); + } else { commitHookEffectListUnmount( Passive$1 | HasEffect, finishedWork, @@ -18657,7 +19070,11 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber( case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (current.mode & ProfileMode) { + startPassiveEffectTimer(); + commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); + recordPassiveEffectDuration(current); + } else { commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); } @@ -18931,6 +19348,7 @@ var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfi var rootDoesHavePassiveEffects = false; var rootWithPendingPassiveEffects = null; var pendingPassiveEffectsLanes = NoLanes; +var pendingPassiveProfilerEffects = []; // Use these to prevent an infinite loop of nested updates var NESTED_UPDATE_LIMIT = 50; var nestedUpdateCount = 0; @@ -18968,6 +19386,20 @@ function requestUpdateLane(fiber) { if ((mode & ConcurrentMode) === NoMode) { return SyncLane; + } else if ( + (executionContext & RenderContext) !== NoContext && + workInProgressRootRenderLanes !== NoLanes + ) { + // This is a render phase update. These are not officially supported. The + // old behavior is to give this the same "thread" (lanes) as + // whatever is currently rendering. So if you call `setState` on a component + // that happens later in the same render, it will flush. Ideally, we want to + // remove the special case and treat them as if they came from an + // interleaved event. Regardless, this pattern is not officially supported. + // This behavior is only a fallback. The flag only exists until we can roll + // out the setState warning, since existing code might accidentally rely on + // the current behavior. + return pickArbitraryLane(workInProgressRootRenderLanes); } var isTransition = requestCurrentTransition() !== NoTransition; @@ -18997,7 +19429,7 @@ function requestUpdateLane(fiber) { if (updateLane !== NoLane) { return updateLane; - } // This update originated outside React. Ask the host environement for an + } // This update originated outside React. Ask the host environment for an // appropriate priority, based on the type of event. // // The opaque type returned by the host config is internally a lane, so we can @@ -19032,6 +19464,12 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { return null; } + { + if (isDevToolsPresent) { + addFiberToLanesMap(root, fiber, lane); + } + } // Mark that the root has a pending update. + markRootUpdated(root, lane, eventTime); if (root === workInProgressRoot) { @@ -19040,7 +19478,7 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render // phase update. In that case, we don't treat render phase updates as if // they were interleaved, for backwards compat reasons. - { + if ((executionContext & RenderContext) === NoContext) { workInProgressRootUpdatedLanes = mergeLanes( workInProgressRootUpdatedLanes, lane @@ -19152,7 +19590,7 @@ function isInterleavedUpdate(fiber, lane) { // then don't treat this as an interleaved update. This pattern is // accompanied by a warning but we haven't fully deprecated it yet. We can // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled. - deferRenderPhaseUpdateToNextBatch + (executionContext & RenderContext) === NoContext ); } // Use this function to schedule a task for a root. There's only one task per // root; if a task was already scheduled, we'll check to make sure the priority @@ -19263,6 +19701,9 @@ function ensureRootIsScheduled(root, currentTime) { // goes through Scheduler. function performConcurrentWorkOnRoot(root, didTimeout) { + { + resetNestedUpdateFlag(); + } // Since we know we're in a React event, we can clear the current // event time. The next update will compute a new event time. currentEventTime = NoTimestamp; @@ -19285,7 +19726,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) { // there's a new task, or that there's no remaining work on this root. return null; } - } // Determine the next expiration time to work on, using the fields stored + } // Determine the next lanes to work on, using the fields stored // on the root. var lanes = getNextLanes( @@ -19490,6 +19931,10 @@ function markRootSuspended$1(root, suspendedLanes) { // through Scheduler function performSyncWorkOnRoot(root) { + { + syncNestedUpdateFlag(); + } + if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) { throw Error("Should not already be working."); } @@ -19790,6 +20235,22 @@ function renderRootSync(root, lanes) { // and prepare a fresh one. Otherwise we'll continue where we left off. if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + + if (memoizedUpdaters.size > 0) { + restorePendingUpdaters(root, workInProgressRootRenderLanes); + memoizedUpdaters.clear(); + } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. + // If we bailout on this work, we'll move them back (like above). + // It's important to move them now in case the work spawns more work at the same priority with different updaters. + // That way we can keep the current update and future updates separate. + + movePendingFibersToMemoized(root, lanes); + } + } + prepareFreshStack(root, lanes); } @@ -19836,6 +20297,22 @@ function renderRootConcurrent(root, lanes) { // and prepare a fresh one. Otherwise we'll continue where we left off. if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + + if (memoizedUpdaters.size > 0) { + restorePendingUpdaters(root, workInProgressRootRenderLanes); + memoizedUpdaters.clear(); + } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. + // If we bailout on this work, we'll move them back (like above). + // It's important to move them now in case the work spawns more work at the same priority with different updaters. + // That way we can keep the current update and future updates separate. + + movePendingFibersToMemoized(root, lanes); + } + } + resetRenderTimer(); prepareFreshStack(root, lanes); } @@ -19936,7 +20413,7 @@ function completeUnitOfWork(unitOfWork) { // 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(completedWork); // Because this fiber did not complete, don't reset its expiration time. + var _next = unwindWork(completedWork); // Because this fiber did not complete, don't reset its lanes. if (_next !== null) { // If completing this work spawned new work, do that next. We'll come @@ -20081,7 +20558,7 @@ function commitRootImpl(root, renderPriorityLevel) { } } // Check if there are any effects in the whole tree. // TODO: This is left over from the effect list implementation, where we had - // to check for the existence of `firstEffect` to satsify Flow. I think the + // to check for the existence of `firstEffect` to satisfy Flow. I think the // only other reason this optimization exists is because it affects profiling. // Reconsider whether this is necessary. @@ -20120,7 +20597,7 @@ function commitRootImpl(root, renderPriorityLevel) { recordCommitTime(); } - commitMutationEffects(root, finishedWork); + commitMutationEffects(root, finishedWork, lanes); resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after // the mutation phase, so that the previous tree is still current during @@ -20173,6 +20650,9 @@ function commitRootImpl(root, renderPriorityLevel) { } if (includesSomeLane(remainingLanes, SyncLane)) { + { + markNestedUpdateScheduled(); + } // Count the number of times the root synchronously re-renders without // finishing. If there are too many, it indicates an infinite update loop. if (root === rootWithNestedUpdates) { @@ -20186,6 +20666,12 @@ function commitRootImpl(root, renderPriorityLevel) { } onCommitRoot(finishedWork.stateNode, renderPriorityLevel); + + { + if (isDevToolsPresent) { + root.memoizedUpdaters.clear(); + } + } // additional work on this root is scheduled. ensureRootIsScheduled(root, now()); @@ -20249,6 +20735,19 @@ function flushPassiveEffects() { return false; } +function enqueuePendingPassiveProfilerEffect(fiber) { + { + pendingPassiveProfilerEffects.push(fiber); + + if (!rootDoesHavePassiveEffects) { + rootDoesHavePassiveEffects = true; + scheduleCallback(NormalPriority, function() { + flushPassiveEffects(); + return null; + }); + } + } +} function flushPassiveEffectsImpl() { if (rootWithPendingPassiveEffects === null) { @@ -20275,6 +20774,16 @@ function flushPassiveEffectsImpl() { commitPassiveUnmountEffects(root.current); commitPassiveMountEffects(root, root.current); // TODO: Move to commitPassiveMountEffects + { + var profilerEffects = pendingPassiveProfilerEffects; + pendingPassiveProfilerEffects = []; + + for (var i = 0; i < profilerEffects.length; i++) { + var _fiber = profilerEffects[i]; + commitPassiveEffectDurations(root, _fiber); + } + } + { isFlushingPassiveEffects = false; } @@ -20292,6 +20801,12 @@ function flushPassiveEffectsImpl() { onPostCommitRoot(root); + { + var stateNode = root.current.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; + } + return true; } @@ -20440,7 +20955,7 @@ function retryTimedOutBoundary(boundaryFiber, retryLane) { // The boundary fiber (a Suspense component or SuspenseList component) // previously was rendered in its fallback state. One of the promises that // suspended it has resolved, which means at least part of the tree was - // likely unblocked. Try rendering again, at a new expiration time. + // likely unblocked. Try rendering again, at a new lanes. if (retryLane === NoLane) { // TODO: Assign this to `suspenseState.retryLane`? to avoid // unnecessary entanglement? @@ -20862,6 +21377,18 @@ function warnAboutRenderPhaseUpdatesInDEV(fiber) { var IsThisRendererActing = { current: false }; +function restorePendingUpdaters(root, lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + memoizedUpdaters.forEach(function(schedulingFiber) { + addFiberToLanesMap(root, schedulingFiber, lanes); + }); // This function intentionally does not clear memoized updaters. + // Those may still be relevant to the current commit + // and a future one (e.g. Suspense). + } + } +} function warnIfNotScopedWithMatchingAct(fiber) { { if ( @@ -21459,8 +21986,6 @@ var hasBadMapPolyfill; } } -var debugCounter = 1; - function FiberNode(tag, pendingProps, key, mode) { // Instance this.tag = tag; @@ -21516,7 +22041,6 @@ function FiberNode(tag, pendingProps, key, mode) { { // This isn't directly used but is handy for debugging internals: - this._debugID = debugCounter++; this._debugSource = null; this._debugOwner = null; this._debugNeedsRemount = false; @@ -21596,7 +22120,6 @@ function createWorkInProgress(current, pendingProps) { { // DEV-only fields - workInProgress._debugID = current._debugID; workInProgress._debugSource = current._debugSource; workInProgress._debugOwner = current._debugOwner; workInProgress._debugHookTypes = current._debugHookTypes; @@ -21817,7 +22340,13 @@ function createFiberFromTypeAndProps( case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - mode |= StrictLegacyMode | StrictEffectsMode; + mode |= StrictLegacyMode; + + if ((mode & ConcurrentMode) !== NoMode) { + // Strict effects should never run on legacy roots + mode |= StrictEffectsMode; + } + break; case REACT_PROFILER_TYPE: @@ -22059,7 +22588,6 @@ function assignFiberPropertiesInDEV(target, source) { target.treeBaseDuration = source.treeBaseDuration; } - target._debugID = source._debugID; target._debugSource = source._debugSource; target._debugOwner = source._debugOwner; target._debugNeedsRemount = source._debugNeedsRemount; @@ -22091,6 +22619,20 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.entangledLanes = NoLanes; this.entanglements = createLaneMap(NoLanes); + { + this.effectDuration = 0; + this.passiveEffectDuration = 0; + } + + { + this.memoizedUpdaters = new Set(); + var pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []); + + for (var i = 0; i < TotalLanes; i++) { + pendingUpdatersLaneMap.push(new Set()); + } + } + { switch (tag) { case ConcurrentRoot: @@ -22356,6 +22898,14 @@ function getPublicRootInstance(container) { } } +var shouldErrorImpl = function(fiber) { + return null; +}; + +function shouldError(fiber) { + return shouldErrorImpl(fiber); +} + var shouldSuspendImpl = function(fiber) { return false; }; @@ -22370,6 +22920,7 @@ var overrideProps = null; var overridePropsDeletePath = null; var overridePropsRenamePath = null; var scheduleUpdate = null; +var setErrorHandler = null; var setSuspenseHandler = null; { @@ -22557,6 +23108,10 @@ var setSuspenseHandler = null; scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp); }; + setErrorHandler = function(newShouldErrorImpl) { + shouldErrorImpl = newShouldErrorImpl; + }; + setSuspenseHandler = function(newShouldSuspendImpl) { shouldSuspendImpl = newShouldSuspendImpl; }; @@ -22594,6 +23149,7 @@ function injectIntoDevTools(devToolsConfig) { overrideProps: overrideProps, overridePropsDeletePath: overridePropsDeletePath, overridePropsRenamePath: overridePropsRenamePath, + setErrorHandler: setErrorHandler, setSuspenseHandler: setSuspenseHandler, scheduleUpdate: scheduleUpdate, currentDispatcherRef: ReactCurrentDispatcher, diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js b/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js index 945ac922f5b3ea..d368200b81b861 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js @@ -8,7 +8,7 @@ * @nolint * @providesModule ReactNativeRenderer-dev * @preventMunge - * @generated SignedSource<<6bb77061f5834486ffcc964f4e8c0fcf>> + * @generated SignedSource<> */ 'use strict'; @@ -3134,7 +3134,6 @@ var enableProfilerTimer = true; var enableLazyElements = false; var warnAboutStringRefs = false; var enableNewReconciler = false; -var deferRenderPhaseUpdateToNextBatch = true; var enableLazyContextPropagation = false; // Don't change these two values. They're used by React Dev Tools. @@ -4287,9 +4286,6 @@ var DebugTracingMode = var StrictLegacyMode = /* */ 8; -var StrictEffectsMode = - /* */ - 16; // If those values are changed that package should be rebuilt and redeployed. @@ -4922,6 +4918,48 @@ function markRootEntangled(root, entangledLanes) { lanes &= ~lane; } } +function addFiberToLanesMap(root, fiber, lanes) { + if (!isDevToolsPresent) { + return; + } + + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; + + while (lanes > 0) { + var index = laneToIndex(lanes); + var lane = 1 << index; + var updaters = pendingUpdatersLaneMap[index]; + updaters.add(fiber); + lanes &= ~lane; + } +} +function movePendingFibersToMemoized(root, lanes) { + if (!isDevToolsPresent) { + return; + } + + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; + var memoizedUpdaters = root.memoizedUpdaters; + + while (lanes > 0) { + var index = laneToIndex(lanes); + var lane = 1 << index; + var updaters = pendingUpdatersLaneMap[index]; + + if (updaters.size > 0) { + updaters.forEach(function(fiber) { + var alternate = fiber.alternate; + + if (alternate === null || !memoizedUpdaters.has(alternate)) { + memoizedUpdaters.add(fiber); + } + }); + updaters.clear(); + } + + lanes &= ~lane; + } +} var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros. Only used on lanes, so assume input is an integer. // Based on: // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 @@ -5985,7 +6023,7 @@ var Passive$1 = /* */ 4; -var ReactVersion = "17.0.3-experimental-2d8d133e1"; +var ReactVersion = "17.0.3-experimental-0eea57724"; var ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig; var NoTransition = 0; @@ -6769,9 +6807,7 @@ function readContext(context) { lastContextDependency = contextItem; currentlyRenderingFiber.dependencies = { lanes: NoLanes, - firstContext: contextItem, - // TODO: This is an old field. Delete it. - responders: null + firstContext: contextItem }; } else { // Append a new context item. @@ -12363,7 +12399,52 @@ var InvalidNestedHooksDispatcherOnRerenderInDEV = null; var now$1 = Scheduler.unstable_now; var commitTime = 0; +var layoutEffectStartTime = -1; var profilerStartTime = -1; +var passiveEffectStartTime = -1; +/** + * Tracks whether the current update was a nested/cascading update (scheduled from a layout effect). + * + * The overall sequence is: + * 1. render + * 2. commit (and call `onRender`, `onCommit`) + * 3. check for nested updates + * 4. flush passive effects (and call `onPostCommit`) + * + * Nested updates are identified in step 3 above, + * but step 4 still applies to the work that was just committed. + * We use two flags to track nested updates then: + * one tracks whether the upcoming update is a nested update, + * and the other tracks whether the current update was a nested update. + * The first value gets synced to the second at the start of the render phase. + */ + +var currentUpdateIsNested = false; +var nestedUpdateScheduled = false; + +function isCurrentUpdateNested() { + return currentUpdateIsNested; +} + +function markNestedUpdateScheduled() { + { + nestedUpdateScheduled = true; + } +} + +function resetNestedUpdateFlag() { + { + currentUpdateIsNested = false; + nestedUpdateScheduled = false; + } +} + +function syncNestedUpdateFlag() { + { + currentUpdateIsNested = nestedUpdateScheduled; + nestedUpdateScheduled = false; + } +} function getCommitTime() { return commitTime; @@ -12398,6 +12479,77 @@ function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { } } +function recordLayoutEffectDuration(fiber) { + if (layoutEffectStartTime >= 0) { + var elapsedTime = now$1() - layoutEffectStartTime; + layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor + // Or the root (for the DevTools Profiler to read) + + var parentFiber = fiber.return; + + while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.effectDuration += elapsedTime; + return; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.effectDuration += elapsedTime; + return; + } + + parentFiber = parentFiber.return; + } + } +} + +function recordPassiveEffectDuration(fiber) { + if (passiveEffectStartTime >= 0) { + var elapsedTime = now$1() - passiveEffectStartTime; + passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor + // Or the root (for the DevTools Profiler to read) + + var parentFiber = fiber.return; + + while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + + if (root !== null) { + root.passiveEffectDuration += elapsedTime; + } + + return; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + + if (parentStateNode !== null) { + // Detached fibers have their state node cleared out. + // In this case, the return pointer is also cleared out, + // so we won't be able to report the time spent in this Profiler's subtree. + parentStateNode.passiveEffectDuration += elapsedTime; + } + + return; + } + + parentFiber = parentFiber.return; + } + } +} + +function startLayoutEffectTimer() { + layoutEffectStartTime = now$1(); +} + +function startPassiveEffectTimer() { + passiveEffectStartTime = now$1(); +} + function transferActualDuration(fiber) { // Transfer time spent rendering these children so we don't lose it // after we rerender. This is used as a helper in special cases @@ -12410,4154 +12562,4219 @@ function transferActualDuration(fiber) { } } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; -var didReceiveUpdate = false; -var didWarnAboutBadClass; -var didWarnAboutModulePatternComponent; -var didWarnAboutContextTypeOnFunctionComponent; -var didWarnAboutGetDerivedStateOnFunctionComponent; -var didWarnAboutFunctionRefs; -var didWarnAboutReassigningProps; -var didWarnAboutRevealOrder; -var didWarnAboutTailOptions; - -{ - didWarnAboutBadClass = {}; - didWarnAboutModulePatternComponent = {}; - didWarnAboutContextTypeOnFunctionComponent = {}; - didWarnAboutGetDerivedStateOnFunctionComponent = {}; - didWarnAboutFunctionRefs = {}; - didWarnAboutReassigningProps = false; - didWarnAboutRevealOrder = {}; - didWarnAboutTailOptions = {}; -} - -function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { - if (current === null) { - // If this is a fresh new component that hasn't been rendered yet, we - // won't update its child set by applying minimal side-effects. Instead, - // we will add them all to the child before it gets rendered. That means - // we can optimize this reconciliation pass by not tracking side-effects. - workInProgress.child = mountChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - } else { - // If the current child is the same as the work in progress, it means that - // we haven't yet started any work on these children. Therefore, we use - // the clone algorithm to create a copy of all the current children. - // If we had any progressed work already, that is invalid at this point so - // let's throw it out. - workInProgress.child = reconcileChildFibers( - workInProgress, - current.child, - nextChildren, - renderLanes - ); - } +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: getStackByFiberInDevAndProd(source) + }; } -function forceUnmountCurrentAndReconcile( - current, - workInProgress, - nextChildren, - renderLanes +if ( + !( + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog === + "function" + ) ) { - // This function is fork of reconcileChildren. It's used in cases where we - // want to reconcile without matching against the existing set. This has the - // effect of all current children being unmounted; even if the type and key - // are the same, the old child is unmounted and a new child is created. - // - // To do this, we're going to go through the reconcile algorithm twice. In - // the first pass, we schedule a deletion for all the current children by - // passing null. - workInProgress.child = reconcileChildFibers( - workInProgress, - current.child, - null, - renderLanes - ); // In the second pass, we mount the new children. The trick here is that we - // pass null in place of where we usually pass the current child set. This has - // the effect of remounting all children regardless of whether their - // identities match. + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +} - workInProgress.child = reconcileChildFibers( - workInProgress, - null, - nextChildren, - renderLanes +function showErrorDialog(boundary, errorInfo) { + var capturedError = { + componentStack: errorInfo.stack !== null ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + boundary !== null && boundary.tag === ClassComponent + ? boundary.stateNode + : null + }; + return ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog( + capturedError ); } -function updateForwardRef( - current, - workInProgress, - Component, - nextProps, - renderLanes -) { - // TODO: current can be non-null here even if the component - // hasn't yet mounted. This happens after the first render suspends. - // We'll need to figure out if this is fine or can cause issues. - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; +function logCapturedError(boundary, errorInfo) { + try { + var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. + // This enables renderers like ReactNative to better manage redbox behavior. - if (innerPropTypes) { - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(Component) - ); - } + if (logError === false) { + return; } - } - var render = Component.render; - var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent - - var nextChildren; - prepareToReadContext(workInProgress, renderLanes); + var error = errorInfo.value; - { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - nextChildren = renderWithHooks( - current, - workInProgress, - render, - nextProps, - ref, - renderLanes - ); + if (true) { + var source = errorInfo.source; + var stack = errorInfo.stack; + var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling + // `preventDefault()` in window `error` handler. + // We record this information as an expando on the error. - setIsRendering(false); - } + if (error != null && error._suppressLogging) { + if (boundary.tag === ClassComponent) { + // The error is recoverable and was silenced. + // Ignore it and don't print the stack addendum. + // This is handy for testing error boundaries without noise. + return; + } // The error is fatal. Since the silencing might have + // been accidental, we'll surface it anyway. + // However, the browser would have silenced the original error + // so we'll print it first, and then print the stack addendum. - if (current !== null && !didReceiveUpdate) { - bailoutHooks(current, workInProgress, renderLanes); - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } // React DevTools reads this flag. + console["error"](error); // Don't transform to our wrapper + // For a more detailed description of this block, see: + // https://github.com/facebook/react/pull/13384 + } - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; + var componentName = source ? getComponentNameFromFiber(source) : null; + var componentNameMessage = componentName + ? "The above error occurred in the <" + componentName + "> component:" + : "The above error occurred in one of your React components:"; + var errorBoundaryMessage; + + if (boundary.tag === HostRoot) { + errorBoundaryMessage = + "Consider adding an error boundary to your tree to customize error handling behavior.\n" + + "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries."; + } else { + var errorBoundaryName = + getComponentNameFromFiber(boundary) || "Anonymous"; + errorBoundaryMessage = + "React will try to recreate this component tree from scratch " + + ("using the error boundary you provided, " + errorBoundaryName + "."); + } + + var combinedMessage = + componentNameMessage + + "\n" + + 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); // Don't transform to our wrapper + } else { + // In production, we print the error directly. + // This will include the message, the JS stack, and anything the browser wants to show. + // We pass the error object instead of custom message so that the browser displays the error natively. + console["error"](error); // Don't transform to our wrapper + } + } catch (e) { + // This method must not throw, or React internal state will get messed up. + // If console.error is overridden, or logCapturedError() shows a dialog that throws, + // we want to report this error outside of the normal stack as a last resort. + // https://github.com/facebook/react/issues/13188 + setTimeout(function() { + throw e; + }); + } } -function updateMemoComponent( - current, - workInProgress, - Component, - nextProps, - updateLanes, - renderLanes -) { - if (current === null) { - var type = Component.type; +var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; - if ( - isSimpleFunctionComponent(type) && - Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. - Component.defaultProps === undefined - ) { - var resolvedType = type; +function createRootErrorUpdate(fiber, errorInfo, lane) { + var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. + + update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property + // being called "element". + + update.payload = { + element: null + }; + var error = errorInfo.value; + + update.callback = function() { + onUncaughtError(error); + logCapturedError(fiber, errorInfo); + }; + + return update; +} + +function createClassErrorUpdate(fiber, errorInfo, lane) { + var update = createUpdate(NoTimestamp, lane); + update.tag = CaptureUpdate; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + + if (typeof getDerivedStateFromError === "function") { + var error$1 = errorInfo.value; + + update.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error$1); + }; + } + + var inst = fiber.stateNode; + if (inst !== null && typeof inst.componentDidCatch === "function") { + update.callback = function callback() { { - resolvedType = resolveFunctionForHotReloading(type); - } // If this is a plain function component without default props, - // and with only the default shallow comparison, we upgrade it - // to a SimpleMemoComponent to allow fast path updates. + markFailedErrorBoundaryForHotReloading(fiber); + } - workInProgress.tag = SimpleMemoComponent; - workInProgress.type = resolvedType; + if (typeof getDerivedStateFromError !== "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 getDerivedStateFromError is + // not defined. + markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined + + logCapturedError(fiber, errorInfo); + } + + var error$1 = errorInfo.value; + var stack = errorInfo.stack; + this.componentDidCatch(error$1, { + componentStack: stack !== null ? stack : "" + }); { - validateFunctionComponentInDev(workInProgress, type); + if (typeof getDerivedStateFromError !== "function") { + // If componentDidCatch is the only error boundary method defined, + // then it needs to call setState to recover from errors. + // If no state update is scheduled then the boundary will swallow the error. + if (!includesSomeLane(fiber.lanes, SyncLane)) { + error( + "%s: Error boundaries should implement getDerivedStateFromError(). " + + "In that method, return a state update to display an error message or fallback UI.", + getComponentNameFromFiber(fiber) || "Unknown" + ); + } + } } + }; + } else { + update.callback = function() { + markFailedErrorBoundaryForHotReloading(fiber); + }; + } - return updateSimpleMemoComponent( - current, - workInProgress, - resolvedType, - nextProps, - updateLanes, - renderLanes - ); + return update; +} + +function attachPingListener(root, wakeable, lanes) { + // Attach a listener to the promise to "ping" the root and retry. But only if + // one does not already exist for the lanes we're currently rendering (which + // acts like a "thread ID" here). + var pingCache = root.pingCache; + var threadIDs; + + if (pingCache === null) { + pingCache = root.pingCache = new PossiblyWeakMap$1(); + threadIDs = new Set(); + pingCache.set(wakeable, threadIDs); + } else { + threadIDs = pingCache.get(wakeable); + + if (threadIDs === undefined) { + threadIDs = new Set(); + pingCache.set(wakeable, threadIDs); } + } - { - var innerPropTypes = type.propTypes; + if (!threadIDs.has(lanes)) { + // Memoize using the thread ID to prevent redundant listeners. + threadIDs.add(lanes); + var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); - if (innerPropTypes) { - // Inner memo component props aren't currently validated in createElement. - // We could move it there, but we'd still need this for lazy code path. - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(type) - ); + { + if (isDevToolsPresent) { + // If we have pending work still, restore the original updaters + restorePendingUpdaters(root, lanes); } } - var child = createFiberFromTypeAndProps( - Component.type, - null, - nextProps, - workInProgress, - workInProgress.mode, - renderLanes - ); - child.ref = workInProgress.ref; - child.return = workInProgress; - workInProgress.child = child; - return child; + wakeable.then(ping, ping); } +} - { - var _type = Component.type; - var _innerPropTypes = _type.propTypes; +function throwException( + root, + returnFiber, + sourceFiber, + value, + rootRenderLanes +) { + // The source fiber did not complete. + sourceFiber.flags |= Incomplete; - if (_innerPropTypes) { - // Inner memo component props aren't currently validated in createElement. - // We could move it there, but we'd still need this for lazy code path. - checkPropTypes( - _innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(_type) - ); + { + if (isDevToolsPresent) { + // If we have pending work still, restore the original updaters + restorePendingUpdaters(root, rootRenderLanes); } } - var currentChild = current.child; // This is always exactly one child + if ( + value !== null && + typeof value === "object" && + typeof value.then === "function" + ) { + var wakeable = value; + // A legacy mode Suspense quirk, only relevant to hook components. - if (!includesSomeLane(updateLanes, renderLanes)) { - // This will be the props with resolved defaultProps, - // unlike current.memoizedProps which will be the unresolved ones. - var prevProps = currentChild.memoizedProps; // Default to shallow comparison + var tag = sourceFiber.tag; - var compare = Component.compare; - compare = compare !== null ? compare : shallowEqual; + if ( + (sourceFiber.mode & ConcurrentMode) === NoMode && + (tag === FunctionComponent || + tag === ForwardRef || + tag === SimpleMemoComponent) + ) { + var currentSource = sourceFiber.alternate; - if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + if (currentSource) { + sourceFiber.updateQueue = currentSource.updateQueue; + sourceFiber.memoizedState = currentSource.memoizedState; + sourceFiber.lanes = currentSource.lanes; + } else { + sourceFiber.updateQueue = null; + sourceFiber.memoizedState = null; + } } - } // React DevTools reads this flag. - workInProgress.flags |= PerformedWork; - var newChild = createWorkInProgress(currentChild, nextProps); - newChild.ref = workInProgress.ref; - newChild.return = workInProgress; - workInProgress.child = newChild; - return newChild; -} - -function updateSimpleMemoComponent( - current, - workInProgress, - Component, - nextProps, - updateLanes, - renderLanes -) { - // TODO: current can be non-null here even if the component - // hasn't yet mounted. This happens when the inner render suspends. - // We'll need to figure out if this is fine or can cause issues. - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var outerMemoType = workInProgress.elementType; - - if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { - // We warn when you define propTypes on lazy() - // so let's just skip over it to find memo() outer wrapper. - // Inner props for memo are validated later. - var lazyComponent = outerMemoType; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - - try { - outerMemoType = init(payload); - } catch (x) { - outerMemoType = null; - } // Inner propTypes will be validated in the function component path. - - var outerPropTypes = outerMemoType && outerMemoType.propTypes; - - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - nextProps, // Resolved (SimpleMemoComponent has no defaultProps) - "prop", - getComponentNameFromType(outerMemoType) - ); - } - } - } - } - - if (current !== null) { - var prevProps = current.memoizedProps; - - if ( - shallowEqual(prevProps, nextProps) && - current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload. - workInProgress.type === current.type - ) { - didReceiveUpdate = false; - - if (!includesSomeLane(renderLanes, updateLanes)) { - // The pending lanes were cleared at the beginning of beginWork. We're - // about to bail out, but there might be other lanes that weren't - // included in the current render. Usually, the priority level of the - // remaining updates is accumlated during the evaluation of the - // component (i.e. when processing the update queue). But since since - // we're bailing out early *without* evaluating the component, we need - // to account for it here, too. Reset to the value of the current fiber. - // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, - // because a MemoComponent fiber does not have hooks or an update queue; - // rather, it wraps around an inner component, which may or may not - // contains hooks. - // TODO: Move the reset at in beginWork out of the common path so that - // this is no longer necessary. - workInProgress.lanes = current.lanes; - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); - } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { - // This is a special case that only exists for legacy mode. - // See https://github.com/facebook/react/pull/19216. - didReceiveUpdate = true; - } - } - } - - return updateFunctionComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes - ); -} - -function updateOffscreenComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; - var nextChildren = nextProps.children; - var prevState = current !== null ? current.memoizedState : null; // If this is not null, this is a cache pool that was carried over from the - // previous render. We will push this to the cache pool context so that we can - // resume in-flight requests. - - var spawnedCachePool = null; - - if ( - nextProps.mode === "hidden" || - nextProps.mode === "unstable-defer-without-hiding" - ) { - // Rendering a hidden tree. - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - // In legacy sync mode, don't defer the subtree. Render it now. - var nextState = { - baseLanes: NoLanes, - cachePool: null - }; - workInProgress.memoizedState = nextState; - pushRenderLanes(workInProgress, renderLanes); - } else if (!includesSomeLane(renderLanes, OffscreenLane)) { - // We're hidden, and we're not rendering at Offscreen. We will bail out - // and resume this tree later. - var nextBaseLanes; - - if (prevState !== null) { - var prevBaseLanes = prevState.baseLanes; - nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); - } else { - nextBaseLanes = renderLanes; - } // Schedule this fiber to re-render at offscreen priority. Then bailout. - - workInProgress.lanes = workInProgress.childLanes = laneToLanes( - OffscreenLane - ); - var _nextState = { - baseLanes: nextBaseLanes, - cachePool: spawnedCachePool - }; - workInProgress.memoizedState = _nextState; - workInProgress.updateQueue = null; // We're about to bail out, but we need to push this to the stack anyway - // to avoid a push/pop misalignment. - - pushRenderLanes(workInProgress, nextBaseLanes); - - return null; - } else { - var _nextState2 = { - baseLanes: NoLanes, - cachePool: null - }; - workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. - - var subtreeRenderLanes = - prevState !== null ? prevState.baseLanes : renderLanes; - pushRenderLanes(workInProgress, subtreeRenderLanes); - } - } else { - // Rendering a visible tree. - var _subtreeRenderLanes; - - if (prevState !== null) { - // We're going from hidden -> visible. - _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); - - workInProgress.memoizedState = null; - } else { - // We weren't previously hidden, and we still aren't, so there's nothing - // special to do. Need to push to the stack regardless, though, to avoid - // a push/pop misalignment. - _subtreeRenderLanes = renderLanes; - } - - pushRenderLanes(workInProgress, _subtreeRenderLanes); - } - - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} // Note: These happen to have identical begin phases, for now. We shouldn't hold -// ourselves to this constraint, though. If the behavior diverges, we should -// fork the function. - -var updateLegacyHiddenComponent = updateOffscreenComponent; - -function updateFragment(current, workInProgress, renderLanes) { - var nextChildren = workInProgress.pendingProps; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} - -function updateMode(current, workInProgress, renderLanes) { - var nextChildren = workInProgress.pendingProps.children; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} - -function updateProfiler(current, workInProgress, renderLanes) { - { - workInProgress.flags |= Update; - } - - var nextProps = workInProgress.pendingProps; - var nextChildren = nextProps.children; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} - -function markRef(current, workInProgress) { - var ref = workInProgress.ref; - - if ( - (current === null && ref !== null) || - (current !== null && current.ref !== ref) - ) { - // Schedule a Ref effect - workInProgress.flags |= Ref; - } -} - -function updateFunctionComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes -) { - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; - - if (innerPropTypes) { - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(Component) - ); - } - } - } - - var context; - - { - var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); - context = getMaskedContext(workInProgress, unmaskedContext); - } - - var nextChildren; - prepareToReadContext(workInProgress, renderLanes); - - { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - nextChildren = renderWithHooks( - current, - workInProgress, - Component, - nextProps, - context, - renderLanes - ); - - setIsRendering(false); - } - - if (current !== null && !didReceiveUpdate) { - bailoutHooks(current, workInProgress, renderLanes); - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } // React DevTools reads this flag. + var hasInvisibleParentBoundary = hasSuspenseContext( + suspenseStackCursor.current, + InvisibleParentSuspenseContext + ); // Schedule the nearest Suspense to re-render the timed out view. - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - return workInProgress.child; -} + var _workInProgress = returnFiber; -function updateClassComponent( - current, - workInProgress, - Component, - nextProps, - renderLanes -) { - { - if (workInProgress.type !== workInProgress.elementType) { - // Lazy component props can't be validated in createElement - // because they're only guaranteed to be resolved here. - var innerPropTypes = Component.propTypes; + do { + if ( + _workInProgress.tag === SuspenseComponent && + shouldCaptureSuspense(_workInProgress, hasInvisibleParentBoundary) + ) { + // Found the nearest boundary. + // Stash the promise on the boundary fiber. If the boundary times out, we'll + // attach another listener to flip the boundary back to its normal state. + var wakeables = _workInProgress.updateQueue; - if (innerPropTypes) { - checkPropTypes( - innerPropTypes, - nextProps, // Resolved props - "prop", - getComponentNameFromType(Component) - ); - } - } - } // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. + if (wakeables === null) { + var updateQueue = new Set(); + updateQueue.add(wakeable); + _workInProgress.updateQueue = updateQueue; + } else { + wakeables.add(wakeable); + } // If the boundary is in legacy mode, we should *not* + // suspend the commit. Pretend as if the suspended component rendered + // null and keep rendering. In the commit phase, we'll schedule a + // subsequent synchronous update to re-render the Suspense. + // + // Note: It doesn't matter whether the component that suspended was + // inside a concurrent mode tree. If the Suspense is outside of it, we + // should *not* suspend the commit. + // + // If the suspense boundary suspended itself suspended, we don't have to + // do this trick because nothing was partially started. We can just + // directly do a second pass over the fallback in this render and + // pretend we meant to render that directly. - var hasContext; + if ( + (_workInProgress.mode & ConcurrentMode) === NoMode && + _workInProgress !== returnFiber + ) { + _workInProgress.flags |= DidCapture; + sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. + // But we shouldn't call any lifecycle methods or callbacks. Remove + // all lifecycle effect tags. - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); - } else { - hasContext = false; - } + sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); - prepareToReadContext(workInProgress, renderLanes); - var instance = workInProgress.stateNode; - var shouldUpdate; + if (sourceFiber.tag === ClassComponent) { + var _currentSourceFiber = sourceFiber.alternate; - if (instance === null) { - if (current !== null) { - // A class component without an instance only mounts if it suspended - // inside a non-concurrent tree, in an inconsistent state. We want to - // treat it like a new mount, even though an empty version of it already - // committed. Disconnect the alternate pointers. - current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + if (_currentSourceFiber === null) { + // This is a new mount. Change the tag so it's not mistaken for a + // completed class component. For example, we should not call + // componentWillUnmount if it is deleted. + sourceFiber.tag = IncompleteClassComponent; + } else { + // When we try rendering again, we should not reuse the current fiber, + // since it's known to be in an inconsistent state. Use a force update to + // prevent a bail out. + var update = createUpdate(NoTimestamp, SyncLane); + update.tag = ForceUpdate; + enqueueUpdate(sourceFiber, update); + } + } // The source fiber did not complete. Mark it with Sync priority to + // indicate that it still has pending work. - workInProgress.flags |= Placement; - } // In the initial pass we might need to construct the instance. + sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); // Exit without suspending. - constructClassInstance(workInProgress, Component, nextProps); - mountClassInstance(workInProgress, Component, nextProps, renderLanes); - shouldUpdate = true; - } else if (current === null) { - // In a resume, we'll already have an instance we can reuse. - shouldUpdate = resumeMountClassInstance( - workInProgress, - Component, - nextProps, - renderLanes - ); - } else { - shouldUpdate = updateClassInstance( - current, - workInProgress, - Component, - nextProps, - renderLanes - ); - } + return; + } // Confirmed that the boundary is in a concurrent mode tree. Continue + // with the normal suspend path. + // + // After this we'll use a set of heuristics to determine whether this + // render pass will run to completion or restart or "suspend" the commit. + // The actual logic for this is spread out in different places. + // + // This first principle is that if we're going to suspend when we complete + // a root, then we should also restart if we get an update or ping that + // might unsuspend it, and vice versa. The only reason to suspend is + // because you think you might want to restart before committing. However, + // it doesn't make sense to restart only while in the period we're suspended. + // + // Restarting too aggressively is also not good because it starves out any + // intermediate loading state. So we use heuristics to determine when. + // Suspense Heuristics + // + // If nothing threw a Promise or all the same fallbacks are already showing, + // then don't suspend/restart. + // + // If this is an initial render of a new tree of Suspense boundaries and + // those trigger a fallback, then don't suspend/restart. We want to ensure + // that we can show the initial loading state as quickly as possible. + // + // If we hit a "Delayed" case, such as when we'd switch from content back into + // a fallback, then we should always suspend/restart. Transitions apply + // to this case. If none is defined, JND is used instead. + // + // If we're already showing a fallback and it gets "retried", allowing us to show + // another level, but there's still an inner boundary that would show a fallback, + // then we suspend/restart for 500ms since the last time we showed a fallback + // anywhere in the tree. This effectively throttles progressive loading into a + // consistent train of commits. This also gives us an opportunity to restart to + // get to the completed state slightly earlier. + // + // If there's ambiguity due to batching it's resolved in preference of: + // 1) "delayed", 2) "initial render", 3) "retry". + // + // We want to ensure that a "busy" state doesn't get force committed. We want to + // ensure that new initial loading states can commit as soon as possible. - var nextUnitOfWork = finishClassComponent( - current, - workInProgress, - Component, - shouldUpdate, - hasContext, - renderLanes - ); + attachPingListener(root, wakeable, rootRenderLanes); + _workInProgress.flags |= ShouldCapture; + _workInProgress.lanes = rootRenderLanes; + return; + } // This boundary already captured during this render. Continue to the next + // boundary. - { - var inst = workInProgress.stateNode; + _workInProgress = _workInProgress.return; + } while (_workInProgress !== null); // No boundary was found. Fallthrough to error mode. + // TODO: Use invariant so the message is stripped in prod? - if (shouldUpdate && inst.props !== nextProps) { - if (!didWarnAboutReassigningProps) { - error( - "It looks like %s is reassigning its own `this.props` while rendering. " + - "This is not supported and can lead to confusing bugs.", - getComponentNameFromFiber(workInProgress) || "a component" - ); - } + value = new Error( + (getComponentNameFromFiber(sourceFiber) || "A React component") + + " suspended while rendering, but no fallback UI was specified.\n" + + "\n" + + "Add a component higher in the tree to " + + "provide a loading indicator or placeholder to display." + ); + } // We didn't find a boundary that could handle this type of exception. Start + // over and traverse parent path again, this time treating the exception + // as an error. - didWarnAboutReassigningProps = true; - } - } + renderDidError(); + value = createCapturedValue(value, sourceFiber); + var workInProgress = returnFiber; - return nextUnitOfWork; -} + do { + switch (workInProgress.tag) { + case HostRoot: { + var _errorInfo = value; + workInProgress.flags |= ShouldCapture; + var lane = pickArbitraryLane(rootRenderLanes); + workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); -function finishClassComponent( - current, - workInProgress, - Component, - shouldUpdate, - hasContext, - renderLanes -) { - // Refs should update even if shouldComponentUpdate returns false - markRef(current, workInProgress); - var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; + var _update = createRootErrorUpdate(workInProgress, _errorInfo, lane); - if (!shouldUpdate && !didCaptureError) { - // Context providers should defer to sCU for rendering - if (hasContext) { - invalidateContextProvider(workInProgress, Component, false); - } + enqueueCapturedUpdate(workInProgress, _update); + return; + } - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } + case ClassComponent: + // Capture and retry + var errorInfo = value; + var ctor = workInProgress.type; + var instance = workInProgress.stateNode; - var instance = workInProgress.stateNode; // Rerender + if ( + (workInProgress.flags & DidCapture) === NoFlags && + (typeof ctor.getDerivedStateFromError === "function" || + (instance !== null && + typeof instance.componentDidCatch === "function" && + !isAlreadyFailedLegacyErrorBoundary(instance))) + ) { + workInProgress.flags |= ShouldCapture; - ReactCurrentOwner$1.current = workInProgress; - var nextChildren; + var _lane = pickArbitraryLane(rootRenderLanes); - if ( - didCaptureError && - typeof Component.getDerivedStateFromError !== "function" - ) { - // If we captured an error, but getDerivedStateFromError 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; + workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state - { - stopProfilerTimerIfRunning(); - } - } else { - { - setIsRendering(true); - nextChildren = instance.render(); + var _update2 = createClassErrorUpdate( + workInProgress, + errorInfo, + _lane + ); - setIsRendering(false); + enqueueCapturedUpdate(workInProgress, _update2); + return; + } + + break; } - } // React DevTools reads this flag. - workInProgress.flags |= PerformedWork; + workInProgress = workInProgress.return; + } while (workInProgress !== null); +} - if (current !== null && didCaptureError) { - // If we're recovering from an error, reconcile without reusing any of - // the existing children. Conceptually, the normal children and the children - // that are shown on error are two different sets, so we shouldn't reuse - // normal children even if their identities match. - forceUnmountCurrentAndReconcile( - current, +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var didReceiveUpdate = false; +var didWarnAboutBadClass; +var didWarnAboutModulePatternComponent; +var didWarnAboutContextTypeOnFunctionComponent; +var didWarnAboutGetDerivedStateOnFunctionComponent; +var didWarnAboutFunctionRefs; +var didWarnAboutReassigningProps; +var didWarnAboutRevealOrder; +var didWarnAboutTailOptions; + +{ + didWarnAboutBadClass = {}; + didWarnAboutModulePatternComponent = {}; + didWarnAboutContextTypeOnFunctionComponent = {}; + didWarnAboutGetDerivedStateOnFunctionComponent = {}; + didWarnAboutFunctionRefs = {}; + didWarnAboutReassigningProps = false; + didWarnAboutRevealOrder = {}; + didWarnAboutTailOptions = {}; +} + +function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { + if (current === null) { + // If this is a fresh new component that hasn't been rendered yet, we + // won't update its child set by applying minimal side-effects. Instead, + // we will add them all to the child before it gets rendered. That means + // we can optimize this reconciliation pass by not tracking side-effects. + workInProgress.child = mountChildFibers( workInProgress, + null, nextChildren, renderLanes ); } else { - reconcileChildren(current, workInProgress, nextChildren, renderLanes); - } // Memoize state using the values we just used to render. - // TODO: Restructure so we never read values from the instance. - - workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. - - if (hasContext) { - invalidateContextProvider(workInProgress, Component, true); - } - - return workInProgress.child; -} - -function pushHostRootContext(workInProgress) { - var root = workInProgress.stateNode; - - if (root.pendingContext) { - pushTopLevelContextObject( + // If the current child is the same as the work in progress, it means that + // we haven't yet started any work on these children. Therefore, we use + // the clone algorithm to create a copy of all the current children. + // If we had any progressed work already, that is invalid at this point so + // let's throw it out. + workInProgress.child = reconcileChildFibers( workInProgress, - root.pendingContext, - root.pendingContext !== root.context + current.child, + nextChildren, + renderLanes ); - } else if (root.context) { - // Should always be set - pushTopLevelContextObject(workInProgress, root.context, false); } +} - pushHostContainer(workInProgress, root.containerInfo); +function forceUnmountCurrentAndReconcile( + current, + workInProgress, + nextChildren, + renderLanes +) { + // This function is fork of reconcileChildren. It's used in cases where we + // want to reconcile without matching against the existing set. This has the + // effect of all current children being unmounted; even if the type and key + // are the same, the old child is unmounted and a new child is created. + // + // To do this, we're going to go through the reconcile algorithm twice. In + // the first pass, we schedule a deletion for all the current children by + // passing null. + workInProgress.child = reconcileChildFibers( + workInProgress, + current.child, + null, + renderLanes + ); // In the second pass, we mount the new children. The trick here is that we + // pass null in place of where we usually pass the current child set. This has + // the effect of remounting all children regardless of whether their + // identities match. + + workInProgress.child = reconcileChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); } -function updateHostRoot(current, workInProgress, renderLanes) { - pushHostRootContext(workInProgress); - var updateQueue = workInProgress.updateQueue; +function updateForwardRef( + current, + workInProgress, + Component, + nextProps, + renderLanes +) { + // TODO: current can be non-null here even if the component + // hasn't yet mounted. This happens after the first render suspends. + // We'll need to figure out if this is fine or can cause issues. + { + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; - if (!(current !== null && updateQueue !== null)) { - throw Error( - "If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue." - ); + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) + ); + } + } } - var nextProps = workInProgress.pendingProps; - var prevState = workInProgress.memoizedState; - var prevChildren = prevState.element; - cloneUpdateQueue(current, workInProgress); - processUpdateQueue(workInProgress, nextProps, null, renderLanes); - var nextState = workInProgress.memoizedState; - var root = workInProgress.stateNode; - // being called "element". - - var nextChildren = nextState.element; + var render = Component.render; + var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent - if (nextChildren === prevChildren) { - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } + var nextChildren; + prepareToReadContext(workInProgress, renderLanes); - if (root.hydrate && enterHydrationState()) { - var child = mountChildFibers( + { + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + nextChildren = renderWithHooks( + current, workInProgress, - null, - nextChildren, + render, + nextProps, + ref, renderLanes ); - workInProgress.child = child; - var node = child; - while (node) { - // Mark each child as hydrating. This is a fast path to know whether this - // tree is part of a hydrating tree. This is used to determine if a child - // node has fully mounted yet, and for scheduling event replaying. - // Conceptually this is similar to Placement in that a new subtree is - // inserted into the React tree here. It just happens to not need DOM - // mutations because it already exists. - node.flags = (node.flags & ~Placement) | Hydrating; - node = node.sibling; - } - } else { - // Otherwise reset hydration state in case we aborted and resumed another - // root. - reconcileChildren(current, workInProgress, nextChildren, renderLanes); + setIsRendering(false); } - return workInProgress.child; -} - -function updateHostComponent(current, workInProgress, renderLanes) { - pushHostContext(workInProgress); - - var type = workInProgress.type; - var nextProps = workInProgress.pendingProps; - var prevProps = current !== null ? current.memoizedProps : null; - var nextChildren = nextProps.children; - - if (prevProps !== null && shouldSetTextContent()) { - // If we're switching from a direct text child to a normal child, or to - // empty, we need to schedule the text content to be reset. - workInProgress.flags |= ContentReset; - } + if (current !== null && !didReceiveUpdate) { + bailoutHooks(current, workInProgress, renderLanes); + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } // React DevTools reads this flag. - markRef(current, workInProgress); + workInProgress.flags |= PerformedWork; reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; } -function updateHostText(current, workInProgress) { - // immediately after. - - return null; -} - -function mountLazyComponent( - _current, +function updateMemoComponent( + current, workInProgress, - elementType, + Component, + nextProps, updateLanes, renderLanes ) { - if (_current !== null) { - // A lazy component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - - workInProgress.flags |= Placement; - } - - var props = workInProgress.pendingProps; - var lazyComponent = elementType; - var payload = lazyComponent._payload; - var init = lazyComponent._init; - var Component = init(payload); // Store the unwrapped component in the type. + if (current === null) { + var type = Component.type; - workInProgress.type = Component; - var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); - var resolvedProps = resolveDefaultProps(Component, props); - var child; + if ( + isSimpleFunctionComponent(type) && + Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. + Component.defaultProps === undefined + ) { + var resolvedType = type; - switch (resolvedTag) { - case FunctionComponent: { { - validateFunctionComponentInDev(workInProgress, Component); - workInProgress.type = Component = resolveFunctionForHotReloading( - Component - ); - } + resolvedType = resolveFunctionForHotReloading(type); + } // If this is a plain function component without default props, + // and with only the default shallow comparison, we upgrade it + // to a SimpleMemoComponent to allow fast path updates. - child = updateFunctionComponent( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; - } + workInProgress.tag = SimpleMemoComponent; + workInProgress.type = resolvedType; - case ClassComponent: { { - workInProgress.type = Component = resolveClassForHotReloading( - Component - ); + validateFunctionComponentInDev(workInProgress, type); } - child = updateClassComponent( - null, + return updateSimpleMemoComponent( + current, workInProgress, - Component, - resolvedProps, + resolvedType, + nextProps, + updateLanes, renderLanes ); - return child; } - case ForwardRef: { - { - workInProgress.type = Component = resolveForwardRefForHotReloading( - Component + { + var innerPropTypes = type.propTypes; + + if (innerPropTypes) { + // Inner memo component props aren't currently validated in createElement. + // We could move it there, but we'd still need this for lazy code path. + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(type) ); } - - child = updateForwardRef( - null, - workInProgress, - Component, - resolvedProps, - renderLanes - ); - return child; } - case MemoComponent: { - { - if (workInProgress.type !== workInProgress.elementType) { - var outerPropTypes = Component.propTypes; - - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - resolvedProps, // Resolved for outer only - "prop", - getComponentNameFromType(Component) - ); - } - } - } - - child = updateMemoComponent( - null, - workInProgress, - Component, - resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too - updateLanes, - renderLanes - ); - return child; - } + var child = createFiberFromTypeAndProps( + Component.type, + null, + nextProps, + workInProgress, + workInProgress.mode, + renderLanes + ); + child.ref = workInProgress.ref; + child.return = workInProgress; + workInProgress.child = child; + return child; } - var hint = ""; - - { - if ( - Component !== null && - typeof Component === "object" && - Component.$$typeof === REACT_LAZY_TYPE - ) { - hint = " Did you wrap a component in React.lazy() more than once?"; - } - } // This message intentionally doesn't mention ForwardRef or MemoComponent - // because the fact that it's a separate type of work is an - // implementation detail. - { - throw Error( - "Element type is invalid. Received a promise that resolves to: " + - Component + - ". Lazy element type must resolve to a class or function." + - hint - ); - } -} + var _type = Component.type; + var _innerPropTypes = _type.propTypes; -function mountIncompleteClassComponent( - _current, - workInProgress, - Component, - nextProps, - renderLanes -) { - if (_current !== null) { - // An incomplete component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + if (_innerPropTypes) { + // Inner memo component props aren't currently validated in createElement. + // We could move it there, but we'd still need this for lazy code path. + checkPropTypes( + _innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(_type) + ); + } + } - workInProgress.flags |= Placement; - } // Promote the fiber to a class and try rendering again. + var currentChild = current.child; // This is always exactly one child - workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` - // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. + if (!includesSomeLane(updateLanes, renderLanes)) { + // This will be the props with resolved defaultProps, + // unlike current.memoizedProps which will be the unresolved ones. + var prevProps = currentChild.memoizedProps; // Default to shallow comparison - var hasContext; + var compare = Component.compare; + compare = compare !== null ? compare : shallowEqual; - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); - } else { - hasContext = false; - } + if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } + } // React DevTools reads this flag. - prepareToReadContext(workInProgress, renderLanes); - constructClassInstance(workInProgress, Component, nextProps); - mountClassInstance(workInProgress, Component, nextProps, renderLanes); - return finishClassComponent( - null, - workInProgress, - Component, - true, - hasContext, - renderLanes - ); + workInProgress.flags |= PerformedWork; + var newChild = createWorkInProgress(currentChild, nextProps); + newChild.ref = workInProgress.ref; + newChild.return = workInProgress; + workInProgress.child = newChild; + return newChild; } -function mountIndeterminateComponent( - _current, +function updateSimpleMemoComponent( + current, workInProgress, Component, + nextProps, + updateLanes, renderLanes ) { - if (_current !== null) { - // An indeterminate component only mounts if it suspended inside a non- - // concurrent tree, in an inconsistent state. We want to treat it like - // a new mount, even though an empty version of it already committed. - // Disconnect the alternate pointers. - _current.alternate = null; - workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - - workInProgress.flags |= Placement; - } - - var props = workInProgress.pendingProps; - var context; - + // TODO: current can be non-null here even if the component + // hasn't yet mounted. This happens when the inner render suspends. + // We'll need to figure out if this is fine or can cause issues. { - var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); - context = getMaskedContext(workInProgress, unmaskedContext); - } + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var outerMemoType = workInProgress.elementType; - prepareToReadContext(workInProgress, renderLanes); - var value; + if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { + // We warn when you define propTypes on lazy() + // so let's just skip over it to find memo() outer wrapper. + // Inner props for memo are validated later. + var lazyComponent = outerMemoType; + var payload = lazyComponent._payload; + var init = lazyComponent._init; - { - if ( - Component.prototype && - typeof Component.prototype.render === "function" - ) { - var componentName = getComponentNameFromType(Component) || "Unknown"; + try { + outerMemoType = init(payload); + } catch (x) { + outerMemoType = null; + } // Inner propTypes will be validated in the function component path. - if (!didWarnAboutBadClass[componentName]) { - error( - "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + - "This is likely to cause errors. Change %s to extend React.Component instead.", - componentName, - componentName - ); + var outerPropTypes = outerMemoType && outerMemoType.propTypes; - didWarnAboutBadClass[componentName] = true; + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + nextProps, // Resolved (SimpleMemoComponent has no defaultProps) + "prop", + getComponentNameFromType(outerMemoType) + ); + } } } + } - if (workInProgress.mode & StrictLegacyMode) { - ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); - } - - setIsRendering(true); - ReactCurrentOwner$1.current = workInProgress; - value = renderWithHooks( - null, - workInProgress, - Component, - props, - context, - renderLanes - ); - setIsRendering(false); - } // React DevTools reads this flag. - - workInProgress.flags |= PerformedWork; + if (current !== null) { + var prevProps = current.memoizedProps; - { - // Support for module components is deprecated and is removed behind a flag. - // Whether or not it would crash later, we want to show a good message in DEV first. if ( - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined + shallowEqual(prevProps, nextProps) && + current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload. + workInProgress.type === current.type ) { - var _componentName = getComponentNameFromType(Component) || "Unknown"; + didReceiveUpdate = false; - if (!didWarnAboutModulePatternComponent[_componentName]) { - error( - "The <%s /> component appears to be a function component that returns a class instance. " + - "Change %s to a class that extends React.Component instead. " + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - "cannot be called with `new` by React.", - _componentName, - _componentName, - _componentName + if (!includesSomeLane(renderLanes, updateLanes)) { + // The pending lanes were cleared at the beginning of beginWork. We're + // about to bail out, but there might be other lanes that weren't + // included in the current render. Usually, the priority level of the + // remaining updates is accumulated during the evaluation of the + // component (i.e. when processing the update queue). But since since + // we're bailing out early *without* evaluating the component, we need + // to account for it here, too. Reset to the value of the current fiber. + // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, + // because a MemoComponent fiber does not have hooks or an update queue; + // rather, it wraps around an inner component, which may or may not + // contains hooks. + // TODO: Move the reset at in beginWork out of the common path so that + // this is no longer necessary. + workInProgress.lanes = current.lanes; + return bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes ); - - didWarnAboutModulePatternComponent[_componentName] = true; + } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { + // This is a special case that only exists for legacy mode. + // See https://github.com/facebook/react/pull/19216. + didReceiveUpdate = true; } } } - if ( - // Run these checks in production only if the flag is off. - // Eventually we'll delete this branch altogether. - typeof value === "object" && - value !== null && - typeof value.render === "function" && - value.$$typeof === undefined - ) { - { - var _componentName2 = getComponentNameFromType(Component) || "Unknown"; + return updateFunctionComponent( + current, + workInProgress, + Component, + nextProps, + renderLanes + ); +} - if (!didWarnAboutModulePatternComponent[_componentName2]) { - error( - "The <%s /> component appears to be a function component that returns a class instance. " + - "Change %s to a class that extends React.Component instead. " + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + - "cannot be called with `new` by React.", - _componentName2, - _componentName2, - _componentName2 - ); +function updateOffscreenComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; + var nextChildren = nextProps.children; + var prevState = current !== null ? current.memoizedState : null; // If this is not null, this is a cache pool that was carried over from the + // previous render. We will push this to the cache pool context so that we can + // resume in-flight requests. - didWarnAboutModulePatternComponent[_componentName2] = true; - } - } // Proceed under the assumption that this is a class instance + var spawnedCachePool = null; - workInProgress.tag = ClassComponent; // Throw out any hooks that were used. + if ( + nextProps.mode === "hidden" || + nextProps.mode === "unstable-defer-without-hiding" + ) { + // Rendering a hidden tree. + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + // In legacy sync mode, don't defer the subtree. Render it now. + var nextState = { + baseLanes: NoLanes, + cachePool: null + }; + workInProgress.memoizedState = nextState; + pushRenderLanes(workInProgress, renderLanes); + } else if (!includesSomeLane(renderLanes, OffscreenLane)) { + // We're hidden, and we're not rendering at Offscreen. We will bail out + // and resume this tree later. + var nextBaseLanes; - workInProgress.memoizedState = null; - workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. - // During mounting we don't know the child context yet as the instance doesn't exist. - // We will invalidate the child context in finishClassComponent() right after rendering. + if (prevState !== null) { + var prevBaseLanes = prevState.baseLanes; + nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); + } else { + nextBaseLanes = renderLanes; + } // Schedule this fiber to re-render at offscreen priority. Then bailout. + + workInProgress.lanes = workInProgress.childLanes = laneToLanes( + OffscreenLane + ); + var _nextState = { + baseLanes: nextBaseLanes, + cachePool: spawnedCachePool + }; + workInProgress.memoizedState = _nextState; + workInProgress.updateQueue = null; // We're about to bail out, but we need to push this to the stack anyway + // to avoid a push/pop misalignment. - var hasContext = false; + pushRenderLanes(workInProgress, nextBaseLanes); - if (isContextProvider(Component)) { - hasContext = true; - pushContextProvider(workInProgress); + return null; } else { - hasContext = false; - } + var _nextState2 = { + baseLanes: NoLanes, + cachePool: null + }; + workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. - workInProgress.memoizedState = - value.state !== null && value.state !== undefined ? value.state : null; - initializeUpdateQueue(workInProgress); - adoptClassInstance(workInProgress, value); - mountClassInstance(workInProgress, Component, props, renderLanes); - return finishClassComponent( - null, - workInProgress, - Component, - true, - hasContext, - renderLanes - ); + var subtreeRenderLanes = + prevState !== null ? prevState.baseLanes : renderLanes; + pushRenderLanes(workInProgress, subtreeRenderLanes); + } } else { - // Proceed under the assumption that this is a function component - workInProgress.tag = FunctionComponent; + // Rendering a visible tree. + var _subtreeRenderLanes; - reconcileChildren(null, workInProgress, value, renderLanes); + if (prevState !== null) { + // We're going from hidden -> visible. + _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); - { - validateFunctionComponentInDev(workInProgress, Component); + workInProgress.memoizedState = null; + } else { + // We weren't previously hidden, and we still aren't, so there's nothing + // special to do. Need to push to the stack regardless, though, to avoid + // a push/pop misalignment. + _subtreeRenderLanes = renderLanes; } - return workInProgress.child; + pushRenderLanes(workInProgress, _subtreeRenderLanes); } -} - -function validateFunctionComponentInDev(workInProgress, Component) { - { - if (Component) { - if (Component.childContextTypes) { - error( - "%s(...): childContextTypes cannot be defined on a function component.", - Component.displayName || Component.name || "Component" - ); - } - } - - if (workInProgress.ref !== null) { - var info = ""; - var ownerName = getCurrentFiberOwnerNameInDevOrNull(); - - if (ownerName) { - info += "\n\nCheck the render method of `" + ownerName + "`."; - } - - var warningKey = ownerName || workInProgress._debugID || ""; - var debugSource = workInProgress._debugSource; - - if (debugSource) { - warningKey = debugSource.fileName + ":" + debugSource.lineNumber; - } - - if (!didWarnAboutFunctionRefs[warningKey]) { - didWarnAboutFunctionRefs[warningKey] = true; - - error( - "Function components cannot be given refs. " + - "Attempts to access this ref will fail. " + - "Did you mean to use React.forwardRef()?%s", - info - ); - } - } - if (typeof Component.getDerivedStateFromProps === "function") { - var _componentName3 = getComponentNameFromType(Component) || "Unknown"; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} // Note: These happen to have identical begin phases, for now. We shouldn't hold +// ourselves to this constraint, though. If the behavior diverges, we should +// fork the function. - if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { - error( - "%s: Function components do not support getDerivedStateFromProps.", - _componentName3 - ); +var updateLegacyHiddenComponent = updateOffscreenComponent; - didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; - } - } +function updateFragment(current, workInProgress, renderLanes) { + var nextChildren = workInProgress.pendingProps; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} - if ( - typeof Component.contextType === "object" && - Component.contextType !== null - ) { - var _componentName4 = getComponentNameFromType(Component) || "Unknown"; +function updateMode(current, workInProgress, renderLanes) { + var nextChildren = workInProgress.pendingProps.children; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} - if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { - error( - "%s: Function components do not support contextType.", - _componentName4 - ); +function updateProfiler(current, workInProgress, renderLanes) { + { + workInProgress.flags |= Update; - didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; - } + { + // Reset effect durations for the next eventual effect phase. + // These are reset during render to allow the DevTools commit hook a chance to read them, + var stateNode = workInProgress.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; } } -} - -var SUSPENDED_MARKER = { - dehydrated: null, - retryLane: NoLane -}; -function mountSuspenseOffscreenState(renderLanes) { - return { - baseLanes: renderLanes, - cachePool: getSuspendedCachePool() - }; + var nextProps = workInProgress.pendingProps; + var nextChildren = nextProps.children; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { - var cachePool = null; +function markRef(current, workInProgress) { + var ref = workInProgress.ref; - return { - baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), - cachePool: cachePool - }; -} // TODO: Probably should inline this back + if ( + (current === null && ref !== null) || + (current !== null && current.ref !== ref) + ) { + // Schedule a Ref effect + workInProgress.flags |= Ref; + } +} -function shouldRemainOnFallback( - suspenseContext, +function updateFunctionComponent( current, workInProgress, + Component, + nextProps, renderLanes ) { - // If we're already showing a fallback, there are cases where we need to - // remain on that fallback regardless of whether the content has resolved. - // For example, SuspenseList coordinates when nested content appears. - if (current !== null) { - var suspenseState = current.memoizedState; - - if (suspenseState === null) { - // Currently showing content. Don't hide it, even if ForceSuspenseFallack - // is true. More precise name might be "ForceRemainSuspenseFallback". - // Note: This is a factoring smell. Can't remain on a fallback if there's - // no fallback to remain on. - return false; - } - } // Not currently showing content. Consult the Suspense context. - - return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); -} - -function getRemainingWorkInPrimaryTree(current, renderLanes) { - // TODO: Should not remove render lanes that were pinged during this render - return removeLanes(current.childLanes, renderLanes); -} - -function updateSuspenseComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. - { - if (shouldSuspend(workInProgress)) { - workInProgress.flags |= DidCapture; - } - } - - var suspenseContext = suspenseStackCursor.current; - var showFallback = false; - var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; - if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { - // Something in this boundary's subtree already suspended. Switch to - // rendering the fallback children. - showFallback = true; - workInProgress.flags &= ~DidCapture; - } else { - // Attempting the main content - if (current === null || current.memoizedState !== null) { - // This is a new mount or this boundary is already showing a fallback state. - // Mark this subtree context as having at least one invisible parent that could - // handle the fallback state. - // Boundaries without fallbacks or should be avoided are not considered since - // they cannot handle preferred fallback states. - if ( - nextProps.fallback !== undefined && - nextProps.unstable_avoidThisFallback !== true - ) { - suspenseContext = addSubtreeSuspenseContext( - suspenseContext, - InvisibleParentSuspenseContext + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) ); } } - } - - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense - // boundary's children. This involves some custom reconcilation logic. Two - // main reasons this is so complicated. - // - // First, Legacy Mode has different semantics for backwards compatibility. The - // primary tree will commit in an inconsistent state, so when we do the - // second pass to render the fallback, we do some exceedingly, uh, clever - // hacks to make that not totally break. Like transferring effects and - // deletions from hidden tree. In Concurrent Mode, it's much simpler, - // because we bailout on the primary tree completely and leave it in its old - // state, no effects. Same as what we do for Offscreen (except that - // Offscreen doesn't have the first render pass). - // - // Second is hydration. During hydration, the Suspense fiber has a slightly - // different layout, where the child points to a dehydrated fragment, which - // contains the DOM rendered by the server. - // - // Third, even if you set all that aside, Suspense is like error boundaries in - // that we first we try to render one tree, and if that fails, we render again - // and switch to a different tree. Like a try/catch block. So we have to track - // which branch we're currently rendering. Ideally we would model this using - // a stack. + } - if (current === null) { - // Initial mount - // If we're currently hydrating, try to hydrate this boundary. - // But only if this has a fallback. - if (nextProps.fallback !== undefined); + var context; - var nextPrimaryChildren = nextProps.children; - var nextFallbackChildren = nextProps.fallback; + { + var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); + context = getMaskedContext(workInProgress, unmaskedContext); + } - if (showFallback) { - var fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ); - var primaryChildFragment = workInProgress.child; - primaryChildFragment.memoizedState = mountSuspenseOffscreenState( - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; - return fallbackFragment; - } else if (typeof nextProps.unstable_expectedLoadTime === "number") { - // This is a CPU-bound tree. Skip this tree and show a placeholder to - // unblock the surrounding content. Then immediately retry after the - // initial commit. - var _fallbackFragment = mountSuspenseFallbackChildren( - workInProgress, - nextPrimaryChildren, - nextFallbackChildren, - renderLanes - ); + var nextChildren; + prepareToReadContext(workInProgress, renderLanes); - var _primaryChildFragment = workInProgress.child; - _primaryChildFragment.memoizedState = mountSuspenseOffscreenState( - renderLanes - ); - workInProgress.memoizedState = SUSPENDED_MARKER; // Since nothing actually suspended, there will nothing to ping this to - // get it started back up to attempt the next item. While in terms of - // priority this work has the same priority as this current render, it's - // not part of the same transition once the transition has committed. If - // it's sync, we still want to yield so that it can be painted. - // Conceptually, this is really the same as pinging. We can use any - // RetryLane even if it's the one currently rendering since we're leaving - // it behind on this node. + { + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + nextChildren = renderWithHooks( + current, + workInProgress, + Component, + nextProps, + context, + renderLanes + ); - workInProgress.lanes = SomeRetryLane; - return _fallbackFragment; - } else { - return mountSuspensePrimaryChildren( - workInProgress, - nextPrimaryChildren, - renderLanes - ); - } - } else { - // This is an update. - // If the current fiber has a SuspenseState, that means it's already showing - // a fallback. - var prevState = current.memoizedState; + setIsRendering(false); + } - if (prevState !== null) { - if (showFallback) { - var _nextFallbackChildren2 = nextProps.fallback; - var _nextPrimaryChildren2 = nextProps.children; + if (current !== null && !didReceiveUpdate) { + bailoutHooks(current, workInProgress, renderLanes); + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } // React DevTools reads this flag. - var _fallbackChildFragment = updateSuspenseFallbackChildren( - current, - workInProgress, - _nextPrimaryChildren2, - _nextFallbackChildren2, - renderLanes - ); + workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; +} - var _primaryChildFragment3 = workInProgress.child; - var prevOffscreenState = current.child.memoizedState; - _primaryChildFragment3.memoizedState = - prevOffscreenState === null - ? mountSuspenseOffscreenState(renderLanes) - : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); - _primaryChildFragment3.childLanes = getRemainingWorkInPrimaryTree( - current, - renderLanes +function updateClassComponent( + current, + workInProgress, + Component, + nextProps, + renderLanes +) { + { + // This is used by DevTools to force a boundary to error. + switch (shouldError(workInProgress)) { + case false: { + var _instance = workInProgress.stateNode; + var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack. + // Is there a better way to do this? + + var tempInstance = new ctor( + workInProgress.memoizedProps, + _instance.context ); - workInProgress.memoizedState = SUSPENDED_MARKER; - return _fallbackChildFragment; - } else { - var _nextPrimaryChildren3 = nextProps.children; + var state = tempInstance.state; - var _primaryChildFragment4 = updateSuspensePrimaryChildren( - current, - workInProgress, - _nextPrimaryChildren3, - renderLanes - ); + _instance.updater.enqueueSetState(_instance, state, null); - workInProgress.memoizedState = null; - return _primaryChildFragment4; + break; } - } else { - // The current tree is not already showing a fallback. - if (showFallback) { - // Timed out. - var _nextFallbackChildren3 = nextProps.fallback; - var _nextPrimaryChildren4 = nextProps.children; - var _fallbackChildFragment2 = updateSuspenseFallbackChildren( - current, + case true: { + workInProgress.flags |= DidCapture; + workInProgress.flags |= ShouldCapture; + var error$1 = new Error("Simulated error coming from DevTools"); + var lane = pickArbitraryLane(renderLanes); + workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state + + var update = createClassErrorUpdate( workInProgress, - _nextPrimaryChildren4, - _nextFallbackChildren3, - renderLanes + createCapturedValue(error$1, workInProgress), + lane ); + enqueueCapturedUpdate(workInProgress, update); + break; + } + } - var _primaryChildFragment5 = workInProgress.child; - var _prevOffscreenState = current.child.memoizedState; - _primaryChildFragment5.memoizedState = - _prevOffscreenState === null - ? mountSuspenseOffscreenState(renderLanes) - : updateSuspenseOffscreenState(_prevOffscreenState, renderLanes); - _primaryChildFragment5.childLanes = getRemainingWorkInPrimaryTree( - current, - renderLanes - ); // Skip the primary children, and continue working on the - // fallback children. - - workInProgress.memoizedState = SUSPENDED_MARKER; - return _fallbackChildFragment2; - } else { - // Still haven't timed out. Continue rendering the children, like we - // normally do. - var _nextPrimaryChildren5 = nextProps.children; + if (workInProgress.type !== workInProgress.elementType) { + // Lazy component props can't be validated in createElement + // because they're only guaranteed to be resolved here. + var innerPropTypes = Component.propTypes; - var _primaryChildFragment6 = updateSuspensePrimaryChildren( - current, - workInProgress, - _nextPrimaryChildren5, - renderLanes + if (innerPropTypes) { + checkPropTypes( + innerPropTypes, + nextProps, // Resolved props + "prop", + getComponentNameFromType(Component) ); - - workInProgress.memoizedState = null; - return _primaryChildFragment6; } } + } // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. + + var hasContext; + + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; } -} -function mountSuspensePrimaryChildren( - workInProgress, - primaryChildren, - renderLanes -) { - var mode = workInProgress.mode; - var primaryChildProps = { - mode: "visible", - children: primaryChildren - }; - var primaryChildFragment = createFiberFromOffscreen( - primaryChildProps, - mode, - renderLanes, - null + prepareToReadContext(workInProgress, renderLanes); + var instance = workInProgress.stateNode; + var shouldUpdate; + + if (instance === null) { + if (current !== null) { + // A class component without an instance only mounts if it suspended + // inside a non-concurrent tree, in an inconsistent state. We want to + // treat it like a new mount, even though an empty version of it already + // committed. Disconnect the alternate pointers. + current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect + + workInProgress.flags |= Placement; + } // In the initial pass we might need to construct the instance. + + constructClassInstance(workInProgress, Component, nextProps); + mountClassInstance(workInProgress, Component, nextProps, renderLanes); + shouldUpdate = true; + } else if (current === null) { + // In a resume, we'll already have an instance we can reuse. + shouldUpdate = resumeMountClassInstance( + workInProgress, + Component, + nextProps, + renderLanes + ); + } else { + shouldUpdate = updateClassInstance( + current, + workInProgress, + Component, + nextProps, + renderLanes + ); + } + + var nextUnitOfWork = finishClassComponent( + current, + workInProgress, + Component, + shouldUpdate, + hasContext, + renderLanes ); - primaryChildFragment.return = workInProgress; - workInProgress.child = primaryChildFragment; - return primaryChildFragment; + + { + var inst = workInProgress.stateNode; + + if (shouldUpdate && inst.props !== nextProps) { + if (!didWarnAboutReassigningProps) { + error( + "It looks like %s is reassigning its own `this.props` while rendering. " + + "This is not supported and can lead to confusing bugs.", + getComponentNameFromFiber(workInProgress) || "a component" + ); + } + + didWarnAboutReassigningProps = true; + } + } + + return nextUnitOfWork; } -function mountSuspenseFallbackChildren( +function finishClassComponent( + current, workInProgress, - primaryChildren, - fallbackChildren, + Component, + shouldUpdate, + hasContext, renderLanes ) { - var mode = workInProgress.mode; - var progressedPrimaryFragment = workInProgress.child; - var primaryChildProps = { - mode: "hidden", - children: primaryChildren - }; - var primaryChildFragment; - var fallbackChildFragment; + // Refs should update even if shouldComponentUpdate returns false + markRef(current, workInProgress); + var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; + + if (!shouldUpdate && !didCaptureError) { + // Context providers should defer to sCU for rendering + if (hasContext) { + invalidateContextProvider(workInProgress, Component, false); + } + + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } + + var instance = workInProgress.stateNode; // Rerender + + ReactCurrentOwner$1.current = workInProgress; + var nextChildren; if ( - (mode & ConcurrentMode) === NoMode && - progressedPrimaryFragment !== null + didCaptureError && + typeof Component.getDerivedStateFromError !== "function" ) { - // In legacy mode, we commit the primary tree as if it successfully - // completed, even though it's in an inconsistent state. - primaryChildFragment = progressedPrimaryFragment; - primaryChildFragment.childLanes = NoLanes; - primaryChildFragment.pendingProps = primaryChildProps; + // If we captured an error, but getDerivedStateFromError 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; - if (workInProgress.mode & ProfileMode) { - // Reset the durations from the first pass so they aren't included in the - // final amounts. This seems counterintuitive, since we're intentionally - // not measuring part of the render phase, but this makes it match what we - // do in Concurrent Mode. - primaryChildFragment.actualDuration = 0; - primaryChildFragment.actualStartTime = -1; - primaryChildFragment.selfBaseDuration = 0; - primaryChildFragment.treeBaseDuration = 0; + { + stopProfilerTimerIfRunning(); } + } else { + { + setIsRendering(true); + nextChildren = instance.render(); - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null + setIsRendering(false); + } + } // React DevTools reads this flag. + + workInProgress.flags |= PerformedWork; + + if (current !== null && didCaptureError) { + // If we're recovering from an error, reconcile without reusing any of + // the existing children. Conceptually, the normal children and the children + // that are shown on error are two different sets, so we shouldn't reuse + // normal children even if their identities match. + forceUnmountCurrentAndReconcile( + current, + workInProgress, + nextChildren, + renderLanes ); } else { - primaryChildFragment = createFiberFromOffscreen( - primaryChildProps, - mode, - NoLanes, - null - ); - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null - ); - } + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } // Memoize state using the values we just used to render. + // TODO: Restructure so we never read values from the instance. - primaryChildFragment.return = workInProgress; - fallbackChildFragment.return = workInProgress; - primaryChildFragment.sibling = fallbackChildFragment; - workInProgress.child = primaryChildFragment; - return fallbackChildFragment; -} + workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. -function createWorkInProgressOffscreenFiber(current, offscreenProps) { - // The props argument to `createWorkInProgress` is `any` typed, so we use this - // wrapper function to constrain it. - return createWorkInProgress(current, offscreenProps); + if (hasContext) { + invalidateContextProvider(workInProgress, Component, true); + } + + return workInProgress.child; } -function updateSuspensePrimaryChildren( - current, - workInProgress, - primaryChildren, - renderLanes -) { - var currentPrimaryChildFragment = current.child; - var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; - var primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, - { - mode: "visible", - children: primaryChildren - } - ); +function pushHostRootContext(workInProgress) { + var root = workInProgress.stateNode; - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - primaryChildFragment.lanes = renderLanes; + if (root.pendingContext) { + pushTopLevelContextObject( + workInProgress, + root.pendingContext, + root.pendingContext !== root.context + ); + } else if (root.context) { + // Should always be set + pushTopLevelContextObject(workInProgress, root.context, false); } - primaryChildFragment.return = workInProgress; - primaryChildFragment.sibling = null; + pushHostContainer(workInProgress, root.containerInfo); +} - if (currentFallbackChildFragment !== null) { - // Delete the fallback child fragment - var deletions = workInProgress.deletions; +function updateHostRoot(current, workInProgress, renderLanes) { + pushHostRootContext(workInProgress); + var updateQueue = workInProgress.updateQueue; - if (deletions === null) { - workInProgress.deletions = [currentFallbackChildFragment]; - workInProgress.flags |= ChildDeletion; - } else { - deletions.push(currentFallbackChildFragment); - } + if (!(current !== null && updateQueue !== null)) { + throw Error( + "If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue." + ); } - workInProgress.child = primaryChildFragment; - return primaryChildFragment; -} + var nextProps = workInProgress.pendingProps; + var prevState = workInProgress.memoizedState; + var prevChildren = prevState.element; + cloneUpdateQueue(current, workInProgress); + processUpdateQueue(workInProgress, nextProps, null, renderLanes); + var nextState = workInProgress.memoizedState; + var root = workInProgress.stateNode; + // being called "element". -function updateSuspenseFallbackChildren( - current, - workInProgress, - primaryChildren, - fallbackChildren, - renderLanes -) { - var mode = workInProgress.mode; - var currentPrimaryChildFragment = current.child; - var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; - var primaryChildProps = { - mode: "hidden", - children: primaryChildren - }; - var primaryChildFragment; + var nextChildren = nextState.element; - if ( - // In legacy mode, we commit the primary tree as if it successfully - // completed, even though it's in an inconsistent state. - (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was - // already cloned. In legacy mode, the only case where this isn't true is - // when DevTools forces us to display a fallback; we skip the first render - // pass entirely and go straight to rendering the fallback. (In Concurrent - // Mode, SuspenseList can also trigger this scenario, but this is a legacy- - // only codepath.) - workInProgress.child !== currentPrimaryChildFragment - ) { - var progressedPrimaryFragment = workInProgress.child; - primaryChildFragment = progressedPrimaryFragment; - primaryChildFragment.childLanes = NoLanes; - primaryChildFragment.pendingProps = primaryChildProps; + if (nextChildren === prevChildren) { + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } - if (workInProgress.mode & ProfileMode) { - // Reset the durations from the first pass so they aren't included in the - // final amounts. This seems counterintuitive, since we're intentionally - // not measuring part of the render phase, but this makes it match what we - // do in Concurrent Mode. - primaryChildFragment.actualDuration = 0; - primaryChildFragment.actualStartTime = -1; - primaryChildFragment.selfBaseDuration = - currentPrimaryChildFragment.selfBaseDuration; - primaryChildFragment.treeBaseDuration = - currentPrimaryChildFragment.treeBaseDuration; - } // The fallback fiber was added as a deletion during the first pass. - // However, since we're going to remain on the fallback, we no longer want - // to delete it. + if (root.hydrate && enterHydrationState()) { + var child = mountChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); + workInProgress.child = child; + var node = child; - workInProgress.deletions = null; + while (node) { + // Mark each child as hydrating. This is a fast path to know whether this + // tree is part of a hydrating tree. This is used to determine if a child + // node has fully mounted yet, and for scheduling event replaying. + // Conceptually this is similar to Placement in that a new subtree is + // inserted into the React tree here. It just happens to not need DOM + // mutations because it already exists. + node.flags = (node.flags & ~Placement) | Hydrating; + node = node.sibling; + } } else { - primaryChildFragment = createWorkInProgressOffscreenFiber( - currentPrimaryChildFragment, - primaryChildProps - ); // Since we're reusing a current tree, we need to reuse the flags, too. - // (We don't do this in legacy mode, because in legacy mode we don't re-use - // the current tree; see previous branch.) - - primaryChildFragment.subtreeFlags = - currentPrimaryChildFragment.subtreeFlags & StaticMask; + // Otherwise reset hydration state in case we aborted and resumed another + // root. + reconcileChildren(current, workInProgress, nextChildren, renderLanes); } - var fallbackChildFragment; + return workInProgress.child; +} - if (currentFallbackChildFragment !== null) { - fallbackChildFragment = createWorkInProgress( - currentFallbackChildFragment, - fallbackChildren - ); - } else { - fallbackChildFragment = createFiberFromFragment( - fallbackChildren, - mode, - renderLanes, - null - ); // Needs a placement effect because the parent (the Suspense boundary) already - // mounted but this is a new fiber. +function updateHostComponent(current, workInProgress, renderLanes) { + pushHostContext(workInProgress); - fallbackChildFragment.flags |= Placement; + var type = workInProgress.type; + var nextProps = workInProgress.pendingProps; + var prevProps = current !== null ? current.memoizedProps : null; + var nextChildren = nextProps.children; + + if (prevProps !== null && shouldSetTextContent()) { + // If we're switching from a direct text child to a normal child, or to + // empty, we need to schedule the text content to be reset. + workInProgress.flags |= ContentReset; } - fallbackChildFragment.return = workInProgress; - primaryChildFragment.return = workInProgress; - primaryChildFragment.sibling = fallbackChildFragment; - workInProgress.child = primaryChildFragment; - return fallbackChildFragment; + markRef(current, workInProgress); + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + return workInProgress.child; } -function scheduleWorkOnFiber(fiber, renderLanes) { - fiber.lanes = mergeLanes(fiber.lanes, renderLanes); - var alternate = fiber.alternate; - - if (alternate !== null) { - alternate.lanes = mergeLanes(alternate.lanes, renderLanes); - } +function updateHostText(current, workInProgress) { + // immediately after. - scheduleWorkOnParentPath(fiber.return, renderLanes); + return null; } -function propagateSuspenseContextChange( +function mountLazyComponent( + _current, workInProgress, - firstChild, + elementType, + updateLanes, renderLanes ) { - // Mark any Suspense boundaries with fallbacks as having work to do. - // If they were previously forced into fallbacks, they may now be able - // to unblock. - var node = firstChild; + if (_current !== null) { + // A lazy component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - while (node !== null) { - if (node.tag === SuspenseComponent) { - var state = node.memoizedState; + workInProgress.flags |= Placement; + } - if (state !== null) { - scheduleWorkOnFiber(node, renderLanes); - } - } else if (node.tag === SuspenseListComponent) { - // If the tail is hidden there might not be an Suspense boundaries - // to schedule work on. In this case we have to schedule it on the - // list itself. - // We don't have to traverse to the children of the list since - // the list will propagate the change when it rerenders. - scheduleWorkOnFiber(node, renderLanes); - } else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } + var props = workInProgress.pendingProps; + var lazyComponent = elementType; + var payload = lazyComponent._payload; + var init = lazyComponent._init; + var Component = init(payload); // Store the unwrapped component in the type. - if (node === workInProgress) { - return; - } + workInProgress.type = Component; + var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component)); + var resolvedProps = resolveDefaultProps(Component, props); + var child; - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; + switch (resolvedTag) { + case FunctionComponent: { + { + validateFunctionComponentInDev(workInProgress, Component); + workInProgress.type = Component = resolveFunctionForHotReloading( + Component + ); } - node = node.return; + child = updateFunctionComponent( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; } - node.sibling.return = node.return; - node = node.sibling; - } -} - -function findLastContentRow(firstChild) { - // This is going to find the last row among these children that is already - // showing content on the screen, as opposed to being in fallback state or - // new. If a row has multiple Suspense boundaries, any of them being in the - // fallback state, counts as the whole row being in a fallback state. - // Note that the "rows" will be workInProgress, but any nested children - // will still be current since we haven't rendered them yet. The mounted - // order may not be the same as the new order. We use the new order. - var row = firstChild; - var lastContentRow = null; - - while (row !== null) { - var currentRow = row.alternate; // New rows can't be content rows. + case ClassComponent: { + { + workInProgress.type = Component = resolveClassForHotReloading( + Component + ); + } - if (currentRow !== null && findFirstSuspended(currentRow) === null) { - lastContentRow = row; + child = updateClassComponent( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; } - row = row.sibling; - } + case ForwardRef: { + { + workInProgress.type = Component = resolveForwardRefForHotReloading( + Component + ); + } - return lastContentRow; -} + child = updateForwardRef( + null, + workInProgress, + Component, + resolvedProps, + renderLanes + ); + return child; + } -function validateRevealOrder(revealOrder) { - { - if ( - revealOrder !== undefined && - revealOrder !== "forwards" && - revealOrder !== "backwards" && - revealOrder !== "together" && - !didWarnAboutRevealOrder[revealOrder] - ) { - didWarnAboutRevealOrder[revealOrder] = true; + case MemoComponent: { + { + if (workInProgress.type !== workInProgress.elementType) { + var outerPropTypes = Component.propTypes; - if (typeof revealOrder === "string") { - switch (revealOrder.toLowerCase()) { - case "together": - case "forwards": - case "backwards": { - error( - '"%s" is not a valid value for revealOrder on . ' + - 'Use lowercase "%s" instead.', - revealOrder, - revealOrder.toLowerCase() + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + resolvedProps, // Resolved for outer only + "prop", + getComponentNameFromType(Component) ); - - break; } + } + } - case "forward": - case "backward": { - error( - '"%s" is not a valid value for revealOrder on . ' + - 'React uses the -s suffix in the spelling. Use "%ss" instead.', - revealOrder, - revealOrder.toLowerCase() - ); - - break; - } + child = updateMemoComponent( + null, + workInProgress, + Component, + resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too + updateLanes, + renderLanes + ); + return child; + } + } - default: - error( - '"%s" is not a supported revealOrder on . ' + - 'Did you mean "together", "forwards" or "backwards"?', - revealOrder - ); + var hint = ""; - break; - } - } else { - error( - "%s is not a supported value for revealOrder on . " + - 'Did you mean "together", "forwards" or "backwards"?', - revealOrder - ); - } + { + if ( + Component !== null && + typeof Component === "object" && + Component.$$typeof === REACT_LAZY_TYPE + ) { + hint = " Did you wrap a component in React.lazy() more than once?"; } + } // This message intentionally doesn't mention ForwardRef or MemoComponent + // because the fact that it's a separate type of work is an + // implementation detail. + + { + throw Error( + "Element type is invalid. Received a promise that resolves to: " + + Component + + ". Lazy element type must resolve to a class or function." + + hint + ); } } -function validateTailOptions(tailMode, revealOrder) { - { - if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { - if (tailMode !== "collapsed" && tailMode !== "hidden") { - didWarnAboutTailOptions[tailMode] = true; +function mountIncompleteClassComponent( + _current, + workInProgress, + Component, + nextProps, + renderLanes +) { + if (_current !== null) { + // An incomplete component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - error( - '"%s" is not a supported value for tail on . ' + - 'Did you mean "collapsed" or "hidden"?', - tailMode - ); - } else if (revealOrder !== "forwards" && revealOrder !== "backwards") { - didWarnAboutTailOptions[tailMode] = true; + workInProgress.flags |= Placement; + } // Promote the fiber to a class and try rendering again. - error( - ' is only valid if revealOrder is ' + - '"forwards" or "backwards". ' + - 'Did you mean to specify revealOrder="forwards"?', - tailMode - ); - } - } + workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` + // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. + + var hasContext; + + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; } + + prepareToReadContext(workInProgress, renderLanes); + constructClassInstance(workInProgress, Component, nextProps); + mountClassInstance(workInProgress, Component, nextProps, renderLanes); + return finishClassComponent( + null, + workInProgress, + Component, + true, + hasContext, + renderLanes + ); } -function validateSuspenseListNestedChild(childSlot, index) { - { - var isAnArray = isArray(childSlot); - var isIterable = - !isAnArray && typeof getIteratorFn(childSlot) === "function"; +function mountIndeterminateComponent( + _current, + workInProgress, + Component, + renderLanes +) { + if (_current !== null) { + // An indeterminate component only mounts if it suspended inside a non- + // concurrent tree, in an inconsistent state. We want to treat it like + // a new mount, even though an empty version of it already committed. + // Disconnect the alternate pointers. + _current.alternate = null; + workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect - if (isAnArray || isIterable) { - var type = isAnArray ? "array" : "iterable"; + workInProgress.flags |= Placement; + } - error( - "A nested %s was passed to row #%s in . Wrap it in " + - "an additional SuspenseList to configure its revealOrder: " + - " ... " + - "{%s} ... " + - "", - type, - index, - type - ); + var props = workInProgress.pendingProps; + var context; - return false; - } + { + var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); + context = getMaskedContext(workInProgress, unmaskedContext); } - return true; -} + prepareToReadContext(workInProgress, renderLanes); + var value; -function validateSuspenseListChildren(children, revealOrder) { { if ( - (revealOrder === "forwards" || revealOrder === "backwards") && - children !== undefined && - children !== null && - children !== false + Component.prototype && + typeof Component.prototype.render === "function" ) { - if (isArray(children)) { - for (var i = 0; i < children.length; i++) { - if (!validateSuspenseListNestedChild(children[i], i)) { - return; - } - } - } else { - var iteratorFn = getIteratorFn(children); - - if (typeof iteratorFn === "function") { - var childrenIterator = iteratorFn.call(children); - - if (childrenIterator) { - var step = childrenIterator.next(); - var _i = 0; + var componentName = getComponentNameFromType(Component) || "Unknown"; - for (; !step.done; step = childrenIterator.next()) { - if (!validateSuspenseListNestedChild(step.value, _i)) { - return; - } + if (!didWarnAboutBadClass[componentName]) { + error( + "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + + "This is likely to cause errors. Change %s to extend React.Component instead.", + componentName, + componentName + ); - _i++; - } - } - } else { - error( - 'A single row was passed to a . ' + - "This is not useful since it needs multiple rows. " + - "Did you mean to pass multiple children or an array?", - revealOrder - ); - } + didWarnAboutBadClass[componentName] = true; } } - } -} -function initSuspenseListRenderState( - workInProgress, - isBackwards, - tail, - lastContentRow, - tailMode -) { - var renderState = workInProgress.memoizedState; + if (workInProgress.mode & StrictLegacyMode) { + ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); + } - if (renderState === null) { - workInProgress.memoizedState = { - isBackwards: isBackwards, - rendering: null, - renderingStartTime: 0, - last: lastContentRow, - tail: tail, - tailMode: tailMode - }; - } else { - // We can reuse the existing object from previous renders. - renderState.isBackwards = isBackwards; - renderState.rendering = null; - renderState.renderingStartTime = 0; - renderState.last = lastContentRow; - renderState.tail = tail; - renderState.tailMode = tailMode; - } -} // This can end up rendering this component multiple passes. -// The first pass splits the children fibers into two sets. A head and tail. -// We first render the head. If anything is in fallback state, we do another -// pass through beginWork to rerender all children (including the tail) with -// the force suspend context. If the first render didn't have anything in -// in fallback state. Then we render each row in the tail one-by-one. -// That happens in the completeWork phase without going back to beginWork. + setIsRendering(true); + ReactCurrentOwner$1.current = workInProgress; + value = renderWithHooks( + null, + workInProgress, + Component, + props, + context, + renderLanes + ); + setIsRendering(false); + } // React DevTools reads this flag. -function updateSuspenseListComponent(current, workInProgress, renderLanes) { - var nextProps = workInProgress.pendingProps; - var revealOrder = nextProps.revealOrder; - var tailMode = nextProps.tail; - var newChildren = nextProps.children; - validateRevealOrder(revealOrder); - validateTailOptions(tailMode, revealOrder); - validateSuspenseListChildren(newChildren, revealOrder); - reconcileChildren(current, workInProgress, newChildren, renderLanes); - var suspenseContext = suspenseStackCursor.current; - var shouldForceFallback = hasSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); + workInProgress.flags |= PerformedWork; - if (shouldForceFallback) { - suspenseContext = setShallowSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); - workInProgress.flags |= DidCapture; - } else { - var didSuspendBefore = - current !== null && (current.flags & DidCapture) !== NoFlags; + { + // Support for module components is deprecated and is removed behind a flag. + // Whether or not it would crash later, we want to show a good message in DEV first. + if ( + typeof value === "object" && + value !== null && + typeof value.render === "function" && + value.$$typeof === undefined + ) { + var _componentName = getComponentNameFromType(Component) || "Unknown"; + + if (!didWarnAboutModulePatternComponent[_componentName]) { + error( + "The <%s /> component appears to be a function component that returns a class instance. " + + "Change %s to a class that extends React.Component instead. " + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + + "cannot be called with `new` by React.", + _componentName, + _componentName, + _componentName + ); - if (didSuspendBefore) { - // If we previously forced a fallback, we need to schedule work - // on any nested boundaries to let them know to try to render - // again. This is the same as context updating. - propagateSuspenseContextChange( - workInProgress, - workInProgress.child, - renderLanes - ); + didWarnAboutModulePatternComponent[_componentName] = true; + } } - - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); } - pushSuspenseContext(workInProgress, suspenseContext); + if ( + // Run these checks in production only if the flag is off. + // Eventually we'll delete this branch altogether. + typeof value === "object" && + value !== null && + typeof value.render === "function" && + value.$$typeof === undefined + ) { + { + var _componentName2 = getComponentNameFromType(Component) || "Unknown"; + + if (!didWarnAboutModulePatternComponent[_componentName2]) { + error( + "The <%s /> component appears to be a function component that returns a class instance. " + + "Change %s to a class that extends React.Component instead. " + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + + "cannot be called with `new` by React.", + _componentName2, + _componentName2, + _componentName2 + ); + + didWarnAboutModulePatternComponent[_componentName2] = true; + } + } // Proceed under the assumption that this is a class instance + + workInProgress.tag = ClassComponent; // Throw out any hooks that were used. - if ((workInProgress.mode & ConcurrentMode) === NoMode) { - // In legacy mode, SuspenseList doesn't work so we just - // use make it a noop by treating it as the default revealOrder. workInProgress.memoizedState = null; + workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. + // During mounting we don't know the child context yet as the instance doesn't exist. + // We will invalidate the child context in finishClassComponent() right after rendering. + + var hasContext = false; + + if (isContextProvider(Component)) { + hasContext = true; + pushContextProvider(workInProgress); + } else { + hasContext = false; + } + + workInProgress.memoizedState = + value.state !== null && value.state !== undefined ? value.state : null; + initializeUpdateQueue(workInProgress); + adoptClassInstance(workInProgress, value); + mountClassInstance(workInProgress, Component, props, renderLanes); + return finishClassComponent( + null, + workInProgress, + Component, + true, + hasContext, + renderLanes + ); } else { - switch (revealOrder) { - case "forwards": { - var lastContentRow = findLastContentRow(workInProgress.child); - var tail; + // Proceed under the assumption that this is a function component + workInProgress.tag = FunctionComponent; - if (lastContentRow === null) { - // The whole list is part of the tail. - // TODO: We could fast path by just rendering the tail now. - tail = workInProgress.child; - workInProgress.child = null; - } else { - // Disconnect the tail rows after the content row. - // We're going to render them separately later. - tail = lastContentRow.sibling; - lastContentRow.sibling = null; - } + reconcileChildren(null, workInProgress, value, renderLanes); - initSuspenseListRenderState( - workInProgress, - false, // isBackwards - tail, - lastContentRow, - tailMode + { + validateFunctionComponentInDev(workInProgress, Component); + } + + return workInProgress.child; + } +} + +function validateFunctionComponentInDev(workInProgress, Component) { + { + if (Component) { + if (Component.childContextTypes) { + error( + "%s(...): childContextTypes cannot be defined on a function component.", + Component.displayName || Component.name || "Component" ); - break; } + } - case "backwards": { - // We're going to find the first row that has existing content. - // At the same time we're going to reverse the list of everything - // we pass in the meantime. That's going to be our tail in reverse - // order. - var _tail = null; - var row = workInProgress.child; - workInProgress.child = null; + if (workInProgress.ref !== null) { + var info = ""; + var ownerName = getCurrentFiberOwnerNameInDevOrNull(); - while (row !== null) { - var currentRow = row.alternate; // New rows can't be content rows. + if (ownerName) { + info += "\n\nCheck the render method of `" + ownerName + "`."; + } - if (currentRow !== null && findFirstSuspended(currentRow) === null) { - // This is the beginning of the main content. - workInProgress.child = row; - break; - } + var warningKey = ownerName || ""; + var debugSource = workInProgress._debugSource; - var nextRow = row.sibling; - row.sibling = _tail; - _tail = row; - row = nextRow; - } // TODO: If workInProgress.child is null, we can continue on the tail immediately. + if (debugSource) { + warningKey = debugSource.fileName + ":" + debugSource.lineNumber; + } - initSuspenseListRenderState( - workInProgress, - true, // isBackwards - _tail, - null, // last - tailMode + if (!didWarnAboutFunctionRefs[warningKey]) { + didWarnAboutFunctionRefs[warningKey] = true; + + error( + "Function components cannot be given refs. " + + "Attempts to access this ref will fail. " + + "Did you mean to use React.forwardRef()?%s", + info ); - break; } + } - case "together": { - initSuspenseListRenderState( - workInProgress, - false, // isBackwards - null, // tail - null, // last - undefined + if (typeof Component.getDerivedStateFromProps === "function") { + var _componentName3 = getComponentNameFromType(Component) || "Unknown"; + + if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { + error( + "%s: Function components do not support getDerivedStateFromProps.", + _componentName3 ); - break; - } - default: { - // The default reveal order is the same as not having - // a boundary. - workInProgress.memoizedState = null; + didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; } } - } - return workInProgress.child; -} + if ( + typeof Component.contextType === "object" && + Component.contextType !== null + ) { + var _componentName4 = getComponentNameFromType(Component) || "Unknown"; -function updatePortalComponent(current, workInProgress, renderLanes) { - pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); - var nextChildren = workInProgress.pendingProps; + if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { + error( + "%s: Function components do not support contextType.", + _componentName4 + ); - if (current === null) { - // Portals are special because we don't append the children during mount - // but at commit. Therefore we need to track insertions which the normal - // flow doesn't do during mount. This doesn't happen at the root because - // the root always starts with a "current" with a null child. - // TODO: Consider unifying this with how the root works. - workInProgress.child = reconcileChildFibers( - workInProgress, - null, - nextChildren, - renderLanes - ); - } else { - reconcileChildren(current, workInProgress, nextChildren, renderLanes); + didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; + } + } } +} - return workInProgress.child; +var SUSPENDED_MARKER = { + dehydrated: null, + retryLane: NoLane +}; + +function mountSuspenseOffscreenState(renderLanes) { + return { + baseLanes: renderLanes, + cachePool: getSuspendedCachePool() + }; } -var hasWarnedAboutUsingNoValuePropOnContextProvider = false; +function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { + var cachePool = null; -function updateContextProvider(current, workInProgress, renderLanes) { - var providerType = workInProgress.type; - var context = providerType._context; - var newProps = workInProgress.pendingProps; - var oldProps = workInProgress.memoizedProps; - var newValue = newProps.value; + return { + baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), + cachePool: cachePool + }; +} // TODO: Probably should inline this back - { - if (!("value" in newProps)) { - if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { - hasWarnedAboutUsingNoValuePropOnContextProvider = true; +function shouldRemainOnFallback( + suspenseContext, + current, + workInProgress, + renderLanes +) { + // If we're already showing a fallback, there are cases where we need to + // remain on that fallback regardless of whether the content has resolved. + // For example, SuspenseList coordinates when nested content appears. + if (current !== null) { + var suspenseState = current.memoizedState; - error( - "The `value` prop is required for the ``. Did you misspell it or forget to pass it?" - ); - } + if (suspenseState === null) { + // Currently showing content. Don't hide it, even if ForceSuspenseFallack + // is true. More precise name might be "ForceRemainSuspenseFallback". + // Note: This is a factoring smell. Can't remain on a fallback if there's + // no fallback to remain on. + return false; } + } // Not currently showing content. Consult the Suspense context. - var providerPropTypes = workInProgress.type.propTypes; + return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); +} - if (providerPropTypes) { - checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider"); - } - } +function getRemainingWorkInPrimaryTree(current, renderLanes) { + // TODO: Should not remove render lanes that were pinged during this render + return removeLanes(current.childLanes, renderLanes); +} - pushProvider(workInProgress, context, newValue); +function updateSuspenseComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. { - if (oldProps !== null) { - var oldValue = oldProps.value; + if (shouldSuspend(workInProgress)) { + workInProgress.flags |= DidCapture; + } + } - if (objectIs(oldValue, newValue)) { - // No change. Bailout early if children are the same. - if (oldProps.children === newProps.children && !hasContextChanged()) { - return bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); - } - } else { - // The context value changed. Search for matching consumers and schedule - // them to update. - propagateContextChange(workInProgress, context, renderLanes); + var suspenseContext = suspenseStackCursor.current; + var showFallback = false; + var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; + + if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { + // Something in this boundary's subtree already suspended. Switch to + // rendering the fallback children. + showFallback = true; + workInProgress.flags &= ~DidCapture; + } else { + // Attempting the main content + if (current === null || current.memoizedState !== null) { + // This is a new mount or this boundary is already showing a fallback state. + // Mark this subtree context as having at least one invisible parent that could + // handle the fallback state. + // Boundaries without fallbacks or should be avoided are not considered since + // they cannot handle preferred fallback states. + if ( + nextProps.fallback !== undefined && + nextProps.unstable_avoidThisFallback !== true + ) { + suspenseContext = addSubtreeSuspenseContext( + suspenseContext, + InvisibleParentSuspenseContext + ); } } } - var newChildren = newProps.children; - reconcileChildren(current, workInProgress, newChildren, renderLanes); - return workInProgress.child; -} - -var hasWarnedAboutUsingContextAsConsumer = false; + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense + // boundary's children. This involves some custom reconcilation logic. Two + // main reasons this is so complicated. + // + // First, Legacy Mode has different semantics for backwards compatibility. The + // primary tree will commit in an inconsistent state, so when we do the + // second pass to render the fallback, we do some exceedingly, uh, clever + // hacks to make that not totally break. Like transferring effects and + // deletions from hidden tree. In Concurrent Mode, it's much simpler, + // because we bailout on the primary tree completely and leave it in its old + // state, no effects. Same as what we do for Offscreen (except that + // Offscreen doesn't have the first render pass). + // + // Second is hydration. During hydration, the Suspense fiber has a slightly + // different layout, where the child points to a dehydrated fragment, which + // contains the DOM rendered by the server. + // + // Third, even if you set all that aside, Suspense is like error boundaries in + // that we first we try to render one tree, and if that fails, we render again + // and switch to a different tree. Like a try/catch block. So we have to track + // which branch we're currently rendering. Ideally we would model this using + // a stack. -function updateContextConsumer(current, workInProgress, renderLanes) { - var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In - // DEV mode, we create a separate object for Context.Consumer that acts - // like a proxy to Context. This proxy object adds unnecessary code in PROD - // so we use the old behaviour (Context.Consumer references Context) to - // reduce size and overhead. The separate object references context via - // a property called "_context", which also gives us the ability to check - // in DEV mode if this property exists or not and warn if it does not. + if (current === null) { + // Initial mount + // If we're currently hydrating, try to hydrate this boundary. + // But only if this has a fallback. + if (nextProps.fallback !== undefined); - { - if (context._context === undefined) { - // This may be because it's a Context (rather than a Consumer). - // Or it may be because it's older React where they're the same thing. - // We only want to warn if we're sure it's a new React. - if (context !== context.Consumer) { - if (!hasWarnedAboutUsingContextAsConsumer) { - hasWarnedAboutUsingContextAsConsumer = true; + var nextPrimaryChildren = nextProps.children; + var nextFallbackChildren = nextProps.fallback; - error( - "Rendering directly is not supported and will be removed in " + - "a future major release. Did you mean to render instead?" - ); - } - } - } else { - context = context._context; - } - } + if (showFallback) { + var fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); + var primaryChildFragment = workInProgress.child; + primaryChildFragment.memoizedState = mountSuspenseOffscreenState( + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return fallbackFragment; + } else if (typeof nextProps.unstable_expectedLoadTime === "number") { + // This is a CPU-bound tree. Skip this tree and show a placeholder to + // unblock the surrounding content. Then immediately retry after the + // initial commit. + var _fallbackFragment = mountSuspenseFallbackChildren( + workInProgress, + nextPrimaryChildren, + nextFallbackChildren, + renderLanes + ); - var newProps = workInProgress.pendingProps; - var render = newProps.children; + var _primaryChildFragment = workInProgress.child; + _primaryChildFragment.memoizedState = mountSuspenseOffscreenState( + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; // Since nothing actually suspended, there will nothing to ping this to + // get it started back up to attempt the next item. While in terms of + // priority this work has the same priority as this current render, it's + // not part of the same transition once the transition has committed. If + // it's sync, we still want to yield so that it can be painted. + // Conceptually, this is really the same as pinging. We can use any + // RetryLane even if it's the one currently rendering since we're leaving + // it behind on this node. - { - if (typeof render !== "function") { - error( - "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." + workInProgress.lanes = SomeRetryLane; + return _fallbackFragment; + } else { + return mountSuspensePrimaryChildren( + workInProgress, + nextPrimaryChildren, + renderLanes ); } - } + } else { + // This is an update. + // If the current fiber has a SuspenseState, that means it's already showing + // a fallback. + var prevState = current.memoizedState; - prepareToReadContext(workInProgress, renderLanes); - var newValue = readContext(context); - var newChildren; + if (prevState !== null) { + if (showFallback) { + var _nextFallbackChildren2 = nextProps.fallback; + var _nextPrimaryChildren2 = nextProps.children; - { - ReactCurrentOwner$1.current = workInProgress; - setIsRendering(true); - newChildren = render(newValue); - setIsRendering(false); - } // React DevTools reads this flag. + var _fallbackChildFragment = updateSuspenseFallbackChildren( + current, + workInProgress, + _nextPrimaryChildren2, + _nextFallbackChildren2, + renderLanes + ); - workInProgress.flags |= PerformedWork; - reconcileChildren(current, workInProgress, newChildren, renderLanes); - return workInProgress.child; -} + var _primaryChildFragment3 = workInProgress.child; + var prevOffscreenState = current.child.memoizedState; + _primaryChildFragment3.memoizedState = + prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); + _primaryChildFragment3.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes + ); + workInProgress.memoizedState = SUSPENDED_MARKER; + return _fallbackChildFragment; + } else { + var _nextPrimaryChildren3 = nextProps.children; -function markWorkInProgressReceivedUpdate() { - didReceiveUpdate = true; -} + var _primaryChildFragment4 = updateSuspensePrimaryChildren( + current, + workInProgress, + _nextPrimaryChildren3, + renderLanes + ); -function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { - if (current !== null) { - // Reuse previous dependencies - workInProgress.dependencies = current.dependencies; - } + workInProgress.memoizedState = null; + return _primaryChildFragment4; + } + } else { + // The current tree is not already showing a fallback. + if (showFallback) { + // Timed out. + var _nextFallbackChildren3 = nextProps.fallback; + var _nextPrimaryChildren4 = nextProps.children; + + var _fallbackChildFragment2 = updateSuspenseFallbackChildren( + current, + workInProgress, + _nextPrimaryChildren4, + _nextFallbackChildren3, + renderLanes + ); - { - // Don't update "base" render times for bailouts. - stopProfilerTimerIfRunning(); - } + var _primaryChildFragment5 = workInProgress.child; + var _prevOffscreenState = current.child.memoizedState; + _primaryChildFragment5.memoizedState = + _prevOffscreenState === null + ? mountSuspenseOffscreenState(renderLanes) + : updateSuspenseOffscreenState(_prevOffscreenState, renderLanes); + _primaryChildFragment5.childLanes = getRemainingWorkInPrimaryTree( + current, + renderLanes + ); // Skip the primary children, and continue working on the + // fallback children. - markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. + workInProgress.memoizedState = SUSPENDED_MARKER; + return _fallbackChildFragment2; + } else { + // Still haven't timed out. Continue rendering the children, like we + // normally do. + var _nextPrimaryChildren5 = nextProps.children; - if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { - // The children don't have any work either. We can skip them. - // TODO: Once we add back resuming, we should check if the children are - // a work-in-progress set. If so, we need to transfer their effects. - { - return null; + var _primaryChildFragment6 = updateSuspensePrimaryChildren( + current, + workInProgress, + _nextPrimaryChildren5, + renderLanes + ); + + workInProgress.memoizedState = null; + return _primaryChildFragment6; + } } - } // This fiber doesn't have work, but its subtree does. Clone the child - // fibers and continue. + } +} - cloneChildFibers(current, workInProgress); - return workInProgress.child; +function mountSuspensePrimaryChildren( + workInProgress, + primaryChildren, + renderLanes +) { + var mode = workInProgress.mode; + var primaryChildProps = { + mode: "visible", + children: primaryChildren + }; + var primaryChildFragment = createFiberFromOffscreen( + primaryChildProps, + mode, + renderLanes, + null + ); + primaryChildFragment.return = workInProgress; + workInProgress.child = primaryChildFragment; + return primaryChildFragment; } -function remountFiber(current, oldWorkInProgress, newWorkInProgress) { - { - var returnFiber = oldWorkInProgress.return; +function mountSuspenseFallbackChildren( + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + var mode = workInProgress.mode; + var progressedPrimaryFragment = workInProgress.child; + var primaryChildProps = { + mode: "hidden", + children: primaryChildren + }; + var primaryChildFragment; + var fallbackChildFragment; - if (returnFiber === null) { - throw new Error("Cannot swap the root fiber."); - } // Disconnect from the old current. - // It will get deleted. + if ( + (mode & ConcurrentMode) === NoMode && + progressedPrimaryFragment !== null + ) { + // In legacy mode, we commit the primary tree as if it successfully + // completed, even though it's in an inconsistent state. + primaryChildFragment = progressedPrimaryFragment; + primaryChildFragment.childLanes = NoLanes; + primaryChildFragment.pendingProps = primaryChildProps; - current.alternate = null; - oldWorkInProgress.alternate = null; // Connect to the new tree. + if (workInProgress.mode & ProfileMode) { + // Reset the durations from the first pass so they aren't included in the + // final amounts. This seems counterintuitive, since we're intentionally + // not measuring part of the render phase, but this makes it match what we + // do in Concurrent Mode. + primaryChildFragment.actualDuration = 0; + primaryChildFragment.actualStartTime = -1; + primaryChildFragment.selfBaseDuration = 0; + primaryChildFragment.treeBaseDuration = 0; + } - newWorkInProgress.index = oldWorkInProgress.index; - newWorkInProgress.sibling = oldWorkInProgress.sibling; - newWorkInProgress.return = oldWorkInProgress.return; - newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); + } else { + primaryChildFragment = createFiberFromOffscreen( + primaryChildProps, + mode, + NoLanes, + null + ); + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); + } - if (oldWorkInProgress === returnFiber.child) { - returnFiber.child = newWorkInProgress; - } else { - var prevSibling = returnFiber.child; + primaryChildFragment.return = workInProgress; + fallbackChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; +} - if (prevSibling === null) { - throw new Error("Expected parent to have a child."); - } +function createWorkInProgressOffscreenFiber(current, offscreenProps) { + // The props argument to `createWorkInProgress` is `any` typed, so we use this + // wrapper function to constrain it. + return createWorkInProgress(current, offscreenProps); +} - while (prevSibling.sibling !== oldWorkInProgress) { - prevSibling = prevSibling.sibling; +function updateSuspensePrimaryChildren( + current, + workInProgress, + primaryChildren, + renderLanes +) { + var currentPrimaryChildFragment = current.child; + var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; + var primaryChildFragment = createWorkInProgressOffscreenFiber( + currentPrimaryChildFragment, + { + mode: "visible", + children: primaryChildren + } + ); - if (prevSibling === null) { - throw new Error("Expected to find the previous sibling."); - } - } + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + primaryChildFragment.lanes = renderLanes; + } - prevSibling.sibling = newWorkInProgress; - } // Delete the old fiber and place the new one. - // Since the old fiber is disconnected, we have to schedule it manually. + primaryChildFragment.return = workInProgress; + primaryChildFragment.sibling = null; - var deletions = returnFiber.deletions; + if (currentFallbackChildFragment !== null) { + // Delete the fallback child fragment + var deletions = workInProgress.deletions; if (deletions === null) { - returnFiber.deletions = [current]; - returnFiber.flags |= ChildDeletion; + workInProgress.deletions = [currentFallbackChildFragment]; + workInProgress.flags |= ChildDeletion; } else { - deletions.push(current); + deletions.push(currentFallbackChildFragment); } - - newWorkInProgress.flags |= Placement; // Restart work from the new fiber. - - return newWorkInProgress; } + + workInProgress.child = primaryChildFragment; + return primaryChildFragment; } -function beginWork(current, workInProgress, renderLanes) { - var updateLanes = workInProgress.lanes; +function updateSuspenseFallbackChildren( + current, + workInProgress, + primaryChildren, + fallbackChildren, + renderLanes +) { + var mode = workInProgress.mode; + var currentPrimaryChildFragment = current.child; + var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; + var primaryChildProps = { + mode: "hidden", + children: primaryChildren + }; + var primaryChildFragment; - { - if (workInProgress._debugNeedsRemount && current !== null) { - // This will restart the begin phase with a new fiber. - return remountFiber( - current, - workInProgress, - createFiberFromTypeAndProps( - workInProgress.type, - workInProgress.key, - workInProgress.pendingProps, - workInProgress._debugOwner || null, - workInProgress.mode, - workInProgress.lanes - ) - ); - } - } + if ( + // In legacy mode, we commit the primary tree as if it successfully + // completed, even though it's in an inconsistent state. + (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was + // already cloned. In legacy mode, the only case where this isn't true is + // when DevTools forces us to display a fallback; we skip the first render + // pass entirely and go straight to rendering the fallback. (In Concurrent + // Mode, SuspenseList can also trigger this scenario, but this is a legacy- + // only codepath.) + workInProgress.child !== currentPrimaryChildFragment + ) { + var progressedPrimaryFragment = workInProgress.child; + primaryChildFragment = progressedPrimaryFragment; + primaryChildFragment.childLanes = NoLanes; + primaryChildFragment.pendingProps = primaryChildProps; - if (current !== null) { - var oldProps = current.memoizedProps; - var newProps = workInProgress.pendingProps; + if (workInProgress.mode & ProfileMode) { + // Reset the durations from the first pass so they aren't included in the + // final amounts. This seems counterintuitive, since we're intentionally + // not measuring part of the render phase, but this makes it match what we + // do in Concurrent Mode. + primaryChildFragment.actualDuration = 0; + primaryChildFragment.actualStartTime = -1; + primaryChildFragment.selfBaseDuration = + currentPrimaryChildFragment.selfBaseDuration; + primaryChildFragment.treeBaseDuration = + currentPrimaryChildFragment.treeBaseDuration; + } // The fallback fiber was added as a deletion during the first pass. + // However, since we're going to remain on the fallback, we no longer want + // to delete it. - if ( - oldProps !== newProps || - hasContextChanged() || // Force a re-render if the implementation changed due to hot reload: - workInProgress.type !== current.type - ) { - // If props or context changed, mark the fiber as having performed work. - // This may be unset if the props are determined to be equal later (memo). - didReceiveUpdate = true; - } else if (!includesSomeLane(renderLanes, updateLanes)) { - didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering - // the begin phase. There's still some bookkeeping we that needs to be done - // in this optimized path, mostly pushing stuff onto the stack. + workInProgress.deletions = null; + } else { + primaryChildFragment = createWorkInProgressOffscreenFiber( + currentPrimaryChildFragment, + primaryChildProps + ); // Since we're reusing a current tree, we need to reuse the flags, too. + // (We don't do this in legacy mode, because in legacy mode we don't re-use + // the current tree; see previous branch.) - switch (workInProgress.tag) { - case HostRoot: - pushHostRootContext(workInProgress); - break; + primaryChildFragment.subtreeFlags = + currentPrimaryChildFragment.subtreeFlags & StaticMask; + } - case HostComponent: - pushHostContext(workInProgress); - break; + var fallbackChildFragment; - case ClassComponent: { - var Component = workInProgress.type; + if (currentFallbackChildFragment !== null) { + fallbackChildFragment = createWorkInProgress( + currentFallbackChildFragment, + fallbackChildren + ); + } else { + fallbackChildFragment = createFiberFromFragment( + fallbackChildren, + mode, + renderLanes, + null + ); // Needs a placement effect because the parent (the Suspense boundary) already + // mounted but this is a new fiber. - if (isContextProvider(Component)) { - pushContextProvider(workInProgress); - } + fallbackChildFragment.flags |= Placement; + } - break; - } + fallbackChildFragment.return = workInProgress; + primaryChildFragment.return = workInProgress; + primaryChildFragment.sibling = fallbackChildFragment; + workInProgress.child = primaryChildFragment; + return fallbackChildFragment; +} - case HostPortal: - pushHostContainer( - workInProgress, - workInProgress.stateNode.containerInfo - ); - break; +function scheduleWorkOnFiber(fiber, renderLanes) { + fiber.lanes = mergeLanes(fiber.lanes, renderLanes); + var alternate = fiber.alternate; - case ContextProvider: { - var newValue = workInProgress.memoizedProps.value; - var context = workInProgress.type._context; - pushProvider(workInProgress, context, newValue); - break; - } + if (alternate !== null) { + alternate.lanes = mergeLanes(alternate.lanes, renderLanes); + } - case Profiler: - { - // Profiler should only call onRender when one of its descendants actually rendered. - var hasChildWork = includesSomeLane( - renderLanes, - workInProgress.childLanes - ); + scheduleWorkOnParentPath(fiber.return, renderLanes); +} - if (hasChildWork) { - workInProgress.flags |= Update; - } - } +function propagateSuspenseContextChange( + workInProgress, + firstChild, + renderLanes +) { + // Mark any Suspense boundaries with fallbacks as having work to do. + // If they were previously forced into fallbacks, they may now be able + // to unblock. + var node = firstChild; - break; + while (node !== null) { + if (node.tag === SuspenseComponent) { + var state = node.memoizedState; - case SuspenseComponent: { - var state = workInProgress.memoizedState; + if (state !== null) { + scheduleWorkOnFiber(node, renderLanes); + } + } else if (node.tag === SuspenseListComponent) { + // If the tail is hidden there might not be an Suspense boundaries + // to schedule work on. In this case we have to schedule it on the + // list itself. + // We don't have to traverse to the children of the list since + // the list will propagate the change when it rerenders. + scheduleWorkOnFiber(node, renderLanes); + } else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; + } - if (state !== null) { - // whether to retry the primary children, or to skip over it and - // go straight to the fallback. Check the priority of the primary - // child fragment. + if (node === workInProgress) { + return; + } - var primaryChildFragment = workInProgress.child; - var primaryChildLanes = primaryChildFragment.childLanes; + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; + } - if (includesSomeLane(renderLanes, primaryChildLanes)) { - // The primary children have pending work. Use the normal path - // to attempt to render the primary children again. - return updateSuspenseComponent( - current, - workInProgress, - renderLanes - ); - } else { - // The primary child fragment does not have pending work marked - // on it - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ); // The primary children do not have pending work with sufficient - // priority. Bailout. + node = node.return; + } - var child = bailoutOnAlreadyFinishedWork( - current, - workInProgress, - renderLanes - ); + node.sibling.return = node.return; + node = node.sibling; + } +} - if (child !== null) { - // The fallback children have pending work. Skip over the - // primary children and work on the fallback. - return child.sibling; - } else { - // Note: We can return `null` here because we already checked - // whether there were nested context consumers, via the call to - // `bailoutOnAlreadyFinishedWork` above. - return null; - } - } - } else { - pushSuspenseContext( - workInProgress, - setDefaultShallowSuspenseContext(suspenseStackCursor.current) - ); - } +function findLastContentRow(firstChild) { + // This is going to find the last row among these children that is already + // showing content on the screen, as opposed to being in fallback state or + // new. If a row has multiple Suspense boundaries, any of them being in the + // fallback state, counts as the whole row being in a fallback state. + // Note that the "rows" will be workInProgress, but any nested children + // will still be current since we haven't rendered them yet. The mounted + // order may not be the same as the new order. We use the new order. + var row = firstChild; + var lastContentRow = null; - break; - } + while (row !== null) { + var currentRow = row.alternate; // New rows can't be content rows. - case SuspenseListComponent: { - var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; + if (currentRow !== null && findFirstSuspended(currentRow) === null) { + lastContentRow = row; + } - var _hasChildWork = includesSomeLane( - renderLanes, - workInProgress.childLanes - ); + row = row.sibling; + } - if (didSuspendBefore) { - if (_hasChildWork) { - // If something was in fallback state last time, and we have all the - // same children then we're still in progressive loading state. - // Something might get unblocked by state updates or retries in the - // tree which will affect the tail. So we need to use the normal - // path to compute the correct tail. - return updateSuspenseListComponent( - current, - workInProgress, - renderLanes - ); - } // If none of the children had any work, that means that none of - // them got retried so they'll still be blocked in the same way - // as before. We can fast bail out. + return lastContentRow; +} - workInProgress.flags |= DidCapture; - } // If nothing suspended before and we're rendering the same children, - // then the tail doesn't matter. Anything new that suspends will work - // in the "together" mode, so we can continue from the state we had. +function validateRevealOrder(revealOrder) { + { + if ( + revealOrder !== undefined && + revealOrder !== "forwards" && + revealOrder !== "backwards" && + revealOrder !== "together" && + !didWarnAboutRevealOrder[revealOrder] + ) { + didWarnAboutRevealOrder[revealOrder] = true; - var renderState = workInProgress.memoizedState; + if (typeof revealOrder === "string") { + switch (revealOrder.toLowerCase()) { + case "together": + case "forwards": + case "backwards": { + error( + '"%s" is not a valid value for revealOrder on . ' + + 'Use lowercase "%s" instead.', + revealOrder, + revealOrder.toLowerCase() + ); - if (renderState !== null) { - // Reset to the "together" mode in case we've started a different - // update in the past but didn't complete it. - renderState.rendering = null; - renderState.tail = null; - renderState.lastEffect = null; + break; } - pushSuspenseContext(workInProgress, suspenseStackCursor.current); + case "forward": + case "backward": { + error( + '"%s" is not a valid value for revealOrder on . ' + + 'React uses the -s suffix in the spelling. Use "%ss" instead.', + revealOrder, + revealOrder.toLowerCase() + ); - if (_hasChildWork) { break; - } else { - // If none of the children had any work, that means that none of - // them got retried so they'll still be blocked in the same way - // as before. We can fast bail out. - return null; } - } - case OffscreenComponent: - case LegacyHiddenComponent: { - // Need to check if the tree still needs to be deferred. This is - // almost identical to the logic used in the normal update path, - // so we'll just enter that. The only difference is we'll bail out - // at the next level instead of this one, because the child props - // have not changed. Which is fine. - // TODO: Probably should refactor `beginWork` to split the bailout - // path from the normal path. I'm tempted to do a labeled break here - // but I won't :) - workInProgress.lanes = NoLanes; - return updateOffscreenComponent(current, workInProgress, renderLanes); - } - } + default: + error( + '"%s" is not a supported revealOrder on . ' + + 'Did you mean "together", "forwards" or "backwards"?', + revealOrder + ); - return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); - } else { - if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { - // This is a special case that only exists for legacy mode. - // See https://github.com/facebook/react/pull/19216. - didReceiveUpdate = true; + break; + } } else { - // An update was scheduled on this fiber, but there are no new props - // nor legacy context. Set this to false. If an update queue or context - // consumer produces a changed value, it will set this to true. Otherwise, - // the component will assume the children have not changed and bail out. - didReceiveUpdate = false; + error( + "%s is not a supported value for revealOrder on . " + + 'Did you mean "together", "forwards" or "backwards"?', + revealOrder + ); } } - } else { - didReceiveUpdate = false; - } // Before entering the begin phase, clear pending update priority. - // TODO: This assumes that we're about to evaluate the component and process - // the update queue. However, there's an exception: SimpleMemoComponent - // sometimes bails out later in the begin phase. This indicates that we should - // move this assignment out of the common path and into each branch. + } +} - workInProgress.lanes = NoLanes; +function validateTailOptions(tailMode, revealOrder) { + { + if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { + if (tailMode !== "collapsed" && tailMode !== "hidden") { + didWarnAboutTailOptions[tailMode] = true; - switch (workInProgress.tag) { - case IndeterminateComponent: { - return mountIndeterminateComponent( - current, - workInProgress, - workInProgress.type, - renderLanes - ); + error( + '"%s" is not a supported value for tail on . ' + + 'Did you mean "collapsed" or "hidden"?', + tailMode + ); + } else if (revealOrder !== "forwards" && revealOrder !== "backwards") { + didWarnAboutTailOptions[tailMode] = true; + + error( + ' is only valid if revealOrder is ' + + '"forwards" or "backwards". ' + + 'Did you mean to specify revealOrder="forwards"?', + tailMode + ); + } } + } +} - case LazyComponent: { - var elementType = workInProgress.elementType; - return mountLazyComponent( - current, - workInProgress, - elementType, - updateLanes, - renderLanes +function validateSuspenseListNestedChild(childSlot, index) { + { + var isAnArray = isArray(childSlot); + var isIterable = + !isAnArray && typeof getIteratorFn(childSlot) === "function"; + + if (isAnArray || isIterable) { + var type = isAnArray ? "array" : "iterable"; + + error( + "A nested %s was passed to row #%s in . Wrap it in " + + "an additional SuspenseList to configure its revealOrder: " + + " ... " + + "{%s} ... " + + "", + type, + index, + type ); + + return false; } + } - case FunctionComponent: { - var _Component = workInProgress.type; - var unresolvedProps = workInProgress.pendingProps; - var resolvedProps = - workInProgress.elementType === _Component - ? unresolvedProps - : resolveDefaultProps(_Component, unresolvedProps); - return updateFunctionComponent( - current, - workInProgress, - _Component, - resolvedProps, - renderLanes - ); + return true; +} + +function validateSuspenseListChildren(children, revealOrder) { + { + if ( + (revealOrder === "forwards" || revealOrder === "backwards") && + children !== undefined && + children !== null && + children !== false + ) { + if (isArray(children)) { + for (var i = 0; i < children.length; i++) { + if (!validateSuspenseListNestedChild(children[i], i)) { + return; + } + } + } else { + var iteratorFn = getIteratorFn(children); + + if (typeof iteratorFn === "function") { + var childrenIterator = iteratorFn.call(children); + + if (childrenIterator) { + var step = childrenIterator.next(); + var _i = 0; + + for (; !step.done; step = childrenIterator.next()) { + if (!validateSuspenseListNestedChild(step.value, _i)) { + return; + } + + _i++; + } + } + } else { + error( + 'A single row was passed to a . ' + + "This is not useful since it needs multiple rows. " + + "Did you mean to pass multiple children or an array?", + revealOrder + ); + } + } } + } +} - case ClassComponent: { - var _Component2 = workInProgress.type; - var _unresolvedProps = workInProgress.pendingProps; +function initSuspenseListRenderState( + workInProgress, + isBackwards, + tail, + lastContentRow, + tailMode +) { + var renderState = workInProgress.memoizedState; - var _resolvedProps = - workInProgress.elementType === _Component2 - ? _unresolvedProps - : resolveDefaultProps(_Component2, _unresolvedProps); + if (renderState === null) { + workInProgress.memoizedState = { + isBackwards: isBackwards, + rendering: null, + renderingStartTime: 0, + last: lastContentRow, + tail: tail, + tailMode: tailMode + }; + } else { + // We can reuse the existing object from previous renders. + renderState.isBackwards = isBackwards; + renderState.rendering = null; + renderState.renderingStartTime = 0; + renderState.last = lastContentRow; + renderState.tail = tail; + renderState.tailMode = tailMode; + } +} // This can end up rendering this component multiple passes. +// The first pass splits the children fibers into two sets. A head and tail. +// We first render the head. If anything is in fallback state, we do another +// pass through beginWork to rerender all children (including the tail) with +// the force suspend context. If the first render didn't have anything in +// in fallback state. Then we render each row in the tail one-by-one. +// That happens in the completeWork phase without going back to beginWork. - return updateClassComponent( - current, +function updateSuspenseListComponent(current, workInProgress, renderLanes) { + var nextProps = workInProgress.pendingProps; + var revealOrder = nextProps.revealOrder; + var tailMode = nextProps.tail; + var newChildren = nextProps.children; + validateRevealOrder(revealOrder); + validateTailOptions(tailMode, revealOrder); + validateSuspenseListChildren(newChildren, revealOrder); + reconcileChildren(current, workInProgress, newChildren, renderLanes); + var suspenseContext = suspenseStackCursor.current; + var shouldForceFallback = hasSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + + if (shouldForceFallback) { + suspenseContext = setShallowSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + workInProgress.flags |= DidCapture; + } else { + var didSuspendBefore = + current !== null && (current.flags & DidCapture) !== NoFlags; + + if (didSuspendBefore) { + // If we previously forced a fallback, we need to schedule work + // on any nested boundaries to let them know to try to render + // again. This is the same as context updating. + propagateSuspenseContextChange( workInProgress, - _Component2, - _resolvedProps, + workInProgress.child, renderLanes ); } - case HostRoot: - return updateHostRoot(current, workInProgress, renderLanes); + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + } - case HostComponent: - return updateHostComponent(current, workInProgress, renderLanes); + pushSuspenseContext(workInProgress, suspenseContext); - case HostText: - return updateHostText(); + if ((workInProgress.mode & ConcurrentMode) === NoMode) { + // In legacy mode, SuspenseList doesn't work so we just + // use make it a noop by treating it as the default revealOrder. + workInProgress.memoizedState = null; + } else { + switch (revealOrder) { + case "forwards": { + var lastContentRow = findLastContentRow(workInProgress.child); + var tail; - case SuspenseComponent: - return updateSuspenseComponent(current, workInProgress, renderLanes); + if (lastContentRow === null) { + // The whole list is part of the tail. + // TODO: We could fast path by just rendering the tail now. + tail = workInProgress.child; + workInProgress.child = null; + } else { + // Disconnect the tail rows after the content row. + // We're going to render them separately later. + tail = lastContentRow.sibling; + lastContentRow.sibling = null; + } - case HostPortal: - return updatePortalComponent(current, workInProgress, renderLanes); + initSuspenseListRenderState( + workInProgress, + false, // isBackwards + tail, + lastContentRow, + tailMode + ); + break; + } - case ForwardRef: { - var type = workInProgress.type; - var _unresolvedProps2 = workInProgress.pendingProps; + case "backwards": { + // We're going to find the first row that has existing content. + // At the same time we're going to reverse the list of everything + // we pass in the meantime. That's going to be our tail in reverse + // order. + var _tail = null; + var row = workInProgress.child; + workInProgress.child = null; - var _resolvedProps2 = - workInProgress.elementType === type - ? _unresolvedProps2 - : resolveDefaultProps(type, _unresolvedProps2); + while (row !== null) { + var currentRow = row.alternate; // New rows can't be content rows. - return updateForwardRef( - current, - workInProgress, - type, - _resolvedProps2, - renderLanes - ); - } + if (currentRow !== null && findFirstSuspended(currentRow) === null) { + // This is the beginning of the main content. + workInProgress.child = row; + break; + } - case Fragment: - return updateFragment(current, workInProgress, renderLanes); + var nextRow = row.sibling; + row.sibling = _tail; + _tail = row; + row = nextRow; + } // TODO: If workInProgress.child is null, we can continue on the tail immediately. - case Mode: - return updateMode(current, workInProgress, renderLanes); + initSuspenseListRenderState( + workInProgress, + true, // isBackwards + _tail, + null, // last + tailMode + ); + break; + } - case Profiler: - return updateProfiler(current, workInProgress, renderLanes); + case "together": { + initSuspenseListRenderState( + workInProgress, + false, // isBackwards + null, // tail + null, // last + undefined + ); + break; + } - case ContextProvider: - return updateContextProvider(current, workInProgress, renderLanes); + default: { + // The default reveal order is the same as not having + // a boundary. + workInProgress.memoizedState = null; + } + } + } - case ContextConsumer: - return updateContextConsumer(current, workInProgress, renderLanes); + return workInProgress.child; +} - case MemoComponent: { - var _type2 = workInProgress.type; - var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. +function updatePortalComponent(current, workInProgress, renderLanes) { + pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); + var nextChildren = workInProgress.pendingProps; - var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); + if (current === null) { + // Portals are special because we don't append the children during mount + // but at commit. Therefore we need to track insertions which the normal + // flow doesn't do during mount. This doesn't happen at the root because + // the root always starts with a "current" with a null child. + // TODO: Consider unifying this with how the root works. + workInProgress.child = reconcileChildFibers( + workInProgress, + null, + nextChildren, + renderLanes + ); + } else { + reconcileChildren(current, workInProgress, nextChildren, renderLanes); + } - { - if (workInProgress.type !== workInProgress.elementType) { - var outerPropTypes = _type2.propTypes; + return workInProgress.child; +} - if (outerPropTypes) { - checkPropTypes( - outerPropTypes, - _resolvedProps3, // Resolved for outer only - "prop", - getComponentNameFromType(_type2) - ); - } - } +var hasWarnedAboutUsingNoValuePropOnContextProvider = false; + +function updateContextProvider(current, workInProgress, renderLanes) { + var providerType = workInProgress.type; + var context = providerType._context; + var newProps = workInProgress.pendingProps; + var oldProps = workInProgress.memoizedProps; + var newValue = newProps.value; + + { + if (!("value" in newProps)) { + if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { + hasWarnedAboutUsingNoValuePropOnContextProvider = true; + + error( + "The `value` prop is required for the ``. Did you misspell it or forget to pass it?" + ); } + } - _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); - return updateMemoComponent( - current, - workInProgress, - _type2, - _resolvedProps3, - updateLanes, - renderLanes - ); + var providerPropTypes = workInProgress.type.propTypes; + + if (providerPropTypes) { + checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider"); } + } - case SimpleMemoComponent: { - return updateSimpleMemoComponent( - current, - workInProgress, - workInProgress.type, - workInProgress.pendingProps, - updateLanes, - renderLanes - ); + pushProvider(workInProgress, context, newValue); + + { + if (oldProps !== null) { + var oldValue = oldProps.value; + + if (objectIs(oldValue, newValue)) { + // No change. Bailout early if children are the same. + if (oldProps.children === newProps.children && !hasContextChanged()) { + return bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); + } + } else { + // The context value changed. Search for matching consumers and schedule + // them to update. + propagateContextChange(workInProgress, context, renderLanes); + } } + } - case IncompleteClassComponent: { - var _Component3 = workInProgress.type; - var _unresolvedProps4 = workInProgress.pendingProps; + var newChildren = newProps.children; + reconcileChildren(current, workInProgress, newChildren, renderLanes); + return workInProgress.child; +} - var _resolvedProps4 = - workInProgress.elementType === _Component3 - ? _unresolvedProps4 - : resolveDefaultProps(_Component3, _unresolvedProps4); +var hasWarnedAboutUsingContextAsConsumer = false; + +function updateContextConsumer(current, workInProgress, renderLanes) { + var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In + // DEV mode, we create a separate object for Context.Consumer that acts + // like a proxy to Context. This proxy object adds unnecessary code in PROD + // so we use the old behaviour (Context.Consumer references Context) to + // reduce size and overhead. The separate object references context via + // a property called "_context", which also gives us the ability to check + // in DEV mode if this property exists or not and warn if it does not. + + { + if (context._context === undefined) { + // This may be because it's a Context (rather than a Consumer). + // Or it may be because it's older React where they're the same thing. + // We only want to warn if we're sure it's a new React. + if (context !== context.Consumer) { + if (!hasWarnedAboutUsingContextAsConsumer) { + hasWarnedAboutUsingContextAsConsumer = true; + + error( + "Rendering directly is not supported and will be removed in " + + "a future major release. Did you mean to render instead?" + ); + } + } + } else { + context = context._context; + } + } - return mountIncompleteClassComponent( - current, - workInProgress, - _Component3, - _resolvedProps4, - renderLanes + var newProps = workInProgress.pendingProps; + var render = newProps.children; + + { + if (typeof render !== "function") { + error( + "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." ); } + } - case SuspenseListComponent: { - return updateSuspenseListComponent(current, workInProgress, renderLanes); - } + prepareToReadContext(workInProgress, renderLanes); + var newValue = readContext(context); + var newChildren; - case ScopeComponent: { - break; - } + { + ReactCurrentOwner$1.current = workInProgress; + setIsRendering(true); + newChildren = render(newValue); + setIsRendering(false); + } // React DevTools reads this flag. - case OffscreenComponent: { - return updateOffscreenComponent(current, workInProgress, renderLanes); - } + workInProgress.flags |= PerformedWork; + reconcileChildren(current, workInProgress, newChildren, renderLanes); + return workInProgress.child; +} - case LegacyHiddenComponent: { - return updateLegacyHiddenComponent(current, workInProgress, renderLanes); - } +function markWorkInProgressReceivedUpdate() { + didReceiveUpdate = true; +} + +function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { + if (current !== null) { + // Reuse previous dependencies + workInProgress.dependencies = current.dependencies; } { - throw Error( - "Unknown unit of work tag (" + - workInProgress.tag + - "). This error is likely caused by a bug in React. Please file an issue." - ); + // Don't update "base" render times for bailouts. + stopProfilerTimerIfRunning(); } -} -function markUpdate(workInProgress) { - // Tag the fiber with an update effect. This turns a Placement into - // a PlacementAndUpdate. - workInProgress.flags |= Update; -} + markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. -function markRef$1(workInProgress) { - workInProgress.flags |= Ref; + if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { + // The children don't have any work either. We can skip them. + // TODO: Once we add back resuming, we should check if the children are + // a work-in-progress set. If so, we need to transfer their effects. + { + return null; + } + } // This fiber doesn't have work, but its subtree does. Clone the child + // fibers and continue. + + cloneChildFibers(current, workInProgress); + return workInProgress.child; } -var appendAllChildren; -var updateHostContainer; -var updateHostComponent$1; -var updateHostText$1; +function remountFiber(current, oldWorkInProgress, newWorkInProgress) { + { + var returnFiber = oldWorkInProgress.return; -{ - // Mutation mode - appendAllChildren = function( - parent, - workInProgress, - needsVisibilityToggle, - isHidden - ) { - // We only have the top Fiber that was created but we need recurse down its - // children to find all the terminal nodes. - var node = workInProgress.child; + if (returnFiber === null) { + throw new Error("Cannot swap the root fiber."); + } // Disconnect from the old current. + // It will get deleted. - while (node !== null) { - if (node.tag === HostComponent || node.tag === HostText) { - appendInitialChild(parent, node.stateNode); - } else if (node.tag === HostPortal); - else if (node.child !== null) { - node.child.return = node; - node = node.child; - continue; - } + current.alternate = null; + oldWorkInProgress.alternate = null; // Connect to the new tree. - if (node === workInProgress) { - return; + newWorkInProgress.index = oldWorkInProgress.index; + newWorkInProgress.sibling = oldWorkInProgress.sibling; + newWorkInProgress.return = oldWorkInProgress.return; + newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. + + if (oldWorkInProgress === returnFiber.child) { + returnFiber.child = newWorkInProgress; + } else { + var prevSibling = returnFiber.child; + + if (prevSibling === null) { + throw new Error("Expected parent to have a child."); } - while (node.sibling === null) { - if (node.return === null || node.return === workInProgress) { - return; - } + while (prevSibling.sibling !== oldWorkInProgress) { + prevSibling = prevSibling.sibling; - node = node.return; + if (prevSibling === null) { + throw new Error("Expected to find the previous sibling."); + } } - node.sibling.return = node.return; - node = node.sibling; + prevSibling.sibling = newWorkInProgress; + } // Delete the old fiber and place the new one. + // Since the old fiber is disconnected, we have to schedule it manually. + + var deletions = returnFiber.deletions; + + if (deletions === null) { + returnFiber.deletions = [current]; + returnFiber.flags |= ChildDeletion; + } else { + deletions.push(current); } - }; - updateHostContainer = function(current, workInProgress) { - // Noop - }; + newWorkInProgress.flags |= Placement; // Restart work from the new fiber. - updateHostComponent$1 = function( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ) { - // If we have an alternate, that means this is an update and we need to - // schedule a side-effect to do the updates. + return newWorkInProgress; + } +} + +function beginWork(current, workInProgress, renderLanes) { + var updateLanes = workInProgress.lanes; + + { + if (workInProgress._debugNeedsRemount && current !== null) { + // This will restart the begin phase with a new fiber. + return remountFiber( + current, + workInProgress, + createFiberFromTypeAndProps( + workInProgress.type, + workInProgress.key, + workInProgress.pendingProps, + workInProgress._debugOwner || null, + workInProgress.mode, + workInProgress.lanes + ) + ); + } + } + + if (current !== null) { var oldProps = current.memoizedProps; + var newProps = workInProgress.pendingProps; - if (oldProps === newProps) { - // In mutation mode, this is sufficient for a bailout because - // we won't touch this node even if children changed. - return; - } // If we get updated because one of our children updated, we don't - // have newProps so we'll have to reuse them. - // TODO: Split the update API as separate for the props vs. children. - // Even better would be if children weren't special cased at all tho. + if ( + oldProps !== newProps || + hasContextChanged() || // Force a re-render if the implementation changed due to hot reload: + workInProgress.type !== current.type + ) { + // If props or context changed, mark the fiber as having performed work. + // This may be unset if the props are determined to be equal later (memo). + didReceiveUpdate = true; + } else if (!includesSomeLane(renderLanes, updateLanes)) { + didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering + // the begin phase. There's still some bookkeeping we that needs to be done + // in this optimized path, mostly pushing stuff onto the stack. - 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`. + switch (workInProgress.tag) { + case HostRoot: + pushHostRootContext(workInProgress); + break; - var updatePayload = prepareUpdate(); // TODO: Type this specific to this type of component. + case HostComponent: + pushHostContext(workInProgress); + break; - workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there - // is a new ref we mark this as an update. All the work is done in commitWork. + case ClassComponent: { + var Component = workInProgress.type; - if (updatePayload) { - markUpdate(workInProgress); - } - }; + if (isContextProvider(Component)) { + pushContextProvider(workInProgress); + } - updateHostText$1 = function(current, workInProgress, oldText, newText) { - // If the text differs, mark it as an update. All the work in done in commitWork. - if (oldText !== newText) { - markUpdate(workInProgress); - } - }; -} + break; + } -function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { - switch (renderState.tailMode) { - case "hidden": { - // Any insertions at the end of the tail list after this point - // should be invisible. If there are already mounted boundaries - // anything before them are not considered for collapsing. - // Therefore we need to go through the whole tail to find if - // there are any. - var tailNode = renderState.tail; - var lastTailNode = null; + case HostPortal: + pushHostContainer( + workInProgress, + workInProgress.stateNode.containerInfo + ); + break; - while (tailNode !== null) { - if (tailNode.alternate !== null) { - lastTailNode = tailNode; + case ContextProvider: { + var newValue = workInProgress.memoizedProps.value; + var context = workInProgress.type._context; + pushProvider(workInProgress, context, newValue); + break; } - tailNode = tailNode.sibling; - } // Next we're simply going to delete all insertions after the - // last rendered item. - - if (lastTailNode === null) { - // All remaining items in the tail are insertions. - renderState.tail = null; - } else { - // Detach the insertion after the last node that was already - // inserted. - lastTailNode.sibling = null; - } + case Profiler: + { + // Profiler should only call onRender when one of its descendants actually rendered. + var hasChildWork = includesSomeLane( + renderLanes, + workInProgress.childLanes + ); - break; - } + if (hasChildWork) { + workInProgress.flags |= Update; + } - case "collapsed": { - // Any insertions at the end of the tail list after this point - // should be invisible. If there are already mounted boundaries - // anything before them are not considered for collapsing. - // Therefore we need to go through the whole tail to find if - // there are any. - var _tailNode = renderState.tail; - var _lastTailNode = null; + { + // Reset effect durations for the next eventual effect phase. + // These are reset during render to allow the DevTools commit hook a chance to read them, + var stateNode = workInProgress.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; + } + } - while (_tailNode !== null) { - if (_tailNode.alternate !== null) { - _lastTailNode = _tailNode; - } + break; - _tailNode = _tailNode.sibling; - } // Next we're simply going to delete all insertions after the - // last rendered item. + case SuspenseComponent: { + var state = workInProgress.memoizedState; - if (_lastTailNode === null) { - // All remaining items in the tail are insertions. - if (!hasRenderedATailFallback && renderState.tail !== null) { - // We suspended during the head. We want to show at least one - // row at the tail. So we'll keep on and cut off the rest. - renderState.tail.sibling = null; - } else { - renderState.tail = null; - } - } else { - // Detach the insertion after the last node that was already - // inserted. - _lastTailNode.sibling = null; - } + if (state !== null) { + // whether to retry the primary children, or to skip over it and + // go straight to the fallback. Check the priority of the primary + // child fragment. - break; - } - } -} + var primaryChildFragment = workInProgress.child; + var primaryChildLanes = primaryChildFragment.childLanes; -function bubbleProperties(completedWork) { - var didBailout = - completedWork.alternate !== null && - completedWork.alternate.child === completedWork.child; - var newChildLanes = NoLanes; - var subtreeFlags = NoFlags; + if (includesSomeLane(renderLanes, primaryChildLanes)) { + // The primary children have pending work. Use the normal path + // to attempt to render the primary children again. + return updateSuspenseComponent( + current, + workInProgress, + renderLanes + ); + } else { + // The primary child fragment does not have pending work marked + // on it + pushSuspenseContext( + workInProgress, + setDefaultShallowSuspenseContext(suspenseStackCursor.current) + ); // The primary children do not have pending work with sufficient + // priority. Bailout. - if (!didBailout) { - // Bubble up the earliest expiration time. - if ((completedWork.mode & ProfileMode) !== NoMode) { - // In profiling mode, resetChildExpirationTime is also used to reset - // profiler durations. - var actualDuration = completedWork.actualDuration; - var treeBaseDuration = completedWork.selfBaseDuration; - var child = completedWork.child; + var child = bailoutOnAlreadyFinishedWork( + current, + workInProgress, + renderLanes + ); - while (child !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(child.lanes, child.childLanes) - ); - subtreeFlags |= child.subtreeFlags; - subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will - // only be updated if work is done on the fiber (i.e. it doesn't bailout). - // When work is done, it should bubble to the parent's actualDuration. If - // the fiber has not been cloned though, (meaning no work was done), then - // this value will reflect the amount of time spent working on a previous - // render. In that case it should not bubble. We determine whether it was - // cloned by comparing the child pointer. + if (child !== null) { + // The fallback children have pending work. Skip over the + // primary children and work on the fallback. + return child.sibling; + } else { + // Note: We can return `null` here because we already checked + // whether there were nested context consumers, via the call to + // `bailoutOnAlreadyFinishedWork` above. + return null; + } + } + } else { + pushSuspenseContext( + workInProgress, + setDefaultShallowSuspenseContext(suspenseStackCursor.current) + ); + } - actualDuration += child.actualDuration; - treeBaseDuration += child.treeBaseDuration; - child = child.sibling; - } + break; + } - completedWork.actualDuration = actualDuration; - completedWork.treeBaseDuration = treeBaseDuration; - } else { - var _child = completedWork.child; + case SuspenseListComponent: { + var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; - while (_child !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child.lanes, _child.childLanes) - ); - subtreeFlags |= _child.subtreeFlags; - subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code - // smell because it assumes the commit phase is never concurrent with - // the render phase. Will address during refactor to alternate model. + var _hasChildWork = includesSomeLane( + renderLanes, + workInProgress.childLanes + ); - _child.return = completedWork; - _child = _child.sibling; - } - } + if (didSuspendBefore) { + if (_hasChildWork) { + // If something was in fallback state last time, and we have all the + // same children then we're still in progressive loading state. + // Something might get unblocked by state updates or retries in the + // tree which will affect the tail. So we need to use the normal + // path to compute the correct tail. + return updateSuspenseListComponent( + current, + workInProgress, + renderLanes + ); + } // If none of the children had any work, that means that none of + // them got retried so they'll still be blocked in the same way + // as before. We can fast bail out. - completedWork.subtreeFlags |= subtreeFlags; - } else { - // Bubble up the earliest expiration time. - if ((completedWork.mode & ProfileMode) !== NoMode) { - // In profiling mode, resetChildExpirationTime is also used to reset - // profiler durations. - var _treeBaseDuration = completedWork.selfBaseDuration; - var _child2 = completedWork.child; + workInProgress.flags |= DidCapture; + } // If nothing suspended before and we're rendering the same children, + // then the tail doesn't matter. Anything new that suspends will work + // in the "together" mode, so we can continue from the state we had. - while (_child2 !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child2.lanes, _child2.childLanes) - ); // "Static" flags share the lifetime of the fiber/hook they belong to, - // so we should bubble those up even during a bailout. All the other - // flags have a lifetime only of a single render + commit, so we should - // ignore them. + var renderState = workInProgress.memoizedState; - subtreeFlags |= _child2.subtreeFlags & StaticMask; - subtreeFlags |= _child2.flags & StaticMask; - _treeBaseDuration += _child2.treeBaseDuration; - _child2 = _child2.sibling; - } + if (renderState !== null) { + // Reset to the "together" mode in case we've started a different + // update in the past but didn't complete it. + renderState.rendering = null; + renderState.tail = null; + renderState.lastEffect = null; + } - completedWork.treeBaseDuration = _treeBaseDuration; - } else { - var _child3 = completedWork.child; + pushSuspenseContext(workInProgress, suspenseStackCursor.current); - while (_child3 !== null) { - newChildLanes = mergeLanes( - newChildLanes, - mergeLanes(_child3.lanes, _child3.childLanes) - ); // "Static" flags share the lifetime of the fiber/hook they belong to, - // so we should bubble those up even during a bailout. All the other - // flags have a lifetime only of a single render + commit, so we should - // ignore them. + if (_hasChildWork) { + break; + } else { + // If none of the children had any work, that means that none of + // them got retried so they'll still be blocked in the same way + // as before. We can fast bail out. + return null; + } + } - subtreeFlags |= _child3.subtreeFlags & StaticMask; - subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code - // smell because it assumes the commit phase is never concurrent with - // the render phase. Will address during refactor to alternate model. + case OffscreenComponent: + case LegacyHiddenComponent: { + // Need to check if the tree still needs to be deferred. This is + // almost identical to the logic used in the normal update path, + // so we'll just enter that. The only difference is we'll bail out + // at the next level instead of this one, because the child props + // have not changed. Which is fine. + // TODO: Probably should refactor `beginWork` to split the bailout + // path from the normal path. I'm tempted to do a labeled break here + // but I won't :) + workInProgress.lanes = NoLanes; + return updateOffscreenComponent(current, workInProgress, renderLanes); + } + } - _child3.return = completedWork; - _child3 = _child3.sibling; + return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); + } else { + if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { + // This is a special case that only exists for legacy mode. + // See https://github.com/facebook/react/pull/19216. + didReceiveUpdate = true; + } else { + // An update was scheduled on this fiber, but there are no new props + // nor legacy context. Set this to false. If an update queue or context + // consumer produces a changed value, it will set this to true. Otherwise, + // the component will assume the children have not changed and bail out. + didReceiveUpdate = false; } } + } else { + didReceiveUpdate = false; + } // Before entering the begin phase, clear pending update priority. + // TODO: This assumes that we're about to evaluate the component and process + // the update queue. However, there's an exception: SimpleMemoComponent + // sometimes bails out later in the begin phase. This indicates that we should + // move this assignment out of the common path and into each branch. - completedWork.subtreeFlags |= subtreeFlags; - } + workInProgress.lanes = NoLanes; - completedWork.childLanes = newChildLanes; - return didBailout; -} + switch (workInProgress.tag) { + case IndeterminateComponent: { + return mountIndeterminateComponent( + current, + workInProgress, + workInProgress.type, + renderLanes + ); + } -function completeWork(current, workInProgress, renderLanes) { - var newProps = workInProgress.pendingProps; + case LazyComponent: { + var elementType = workInProgress.elementType; + return mountLazyComponent( + current, + workInProgress, + elementType, + updateLanes, + renderLanes + ); + } - switch (workInProgress.tag) { - case IndeterminateComponent: - case LazyComponent: - case SimpleMemoComponent: - case FunctionComponent: - case ForwardRef: - case Fragment: - case Mode: - case Profiler: - case ContextConsumer: - case MemoComponent: - bubbleProperties(workInProgress); - return null; + case FunctionComponent: { + var _Component = workInProgress.type; + var unresolvedProps = workInProgress.pendingProps; + var resolvedProps = + workInProgress.elementType === _Component + ? unresolvedProps + : resolveDefaultProps(_Component, unresolvedProps); + return updateFunctionComponent( + current, + workInProgress, + _Component, + resolvedProps, + renderLanes + ); + } case ClassComponent: { - var Component = workInProgress.type; + var _Component2 = workInProgress.type; + var _unresolvedProps = workInProgress.pendingProps; - if (isContextProvider(Component)) { - popContext(workInProgress); - } + var _resolvedProps = + workInProgress.elementType === _Component2 + ? _unresolvedProps + : resolveDefaultProps(_Component2, _unresolvedProps); - bubbleProperties(workInProgress); - return null; + return updateClassComponent( + current, + workInProgress, + _Component2, + _resolvedProps, + renderLanes + ); } - case HostRoot: { - var fiberRoot = workInProgress.stateNode; + case HostRoot: + return updateHostRoot(current, workInProgress, renderLanes); - popHostContainer(workInProgress); - popTopLevelContextObject(workInProgress); - resetWorkInProgressVersions(); + case HostComponent: + return updateHostComponent(current, workInProgress, renderLanes); - if (fiberRoot.pendingContext) { - fiberRoot.context = fiberRoot.pendingContext; - fiberRoot.pendingContext = null; - } + case HostText: + return updateHostText(); - if (current === null || current.child === null) { - // If we hydrated, pop so that we can delete any remaining children - // that weren't hydrated. - var wasHydrated = popHydrationState(); + case SuspenseComponent: + return updateSuspenseComponent(current, workInProgress, renderLanes); - if (wasHydrated) { - // If we hydrated, then we'll need to schedule an update for - // the commit side-effects on the root. - markUpdate(workInProgress); - } else if (!fiberRoot.hydrate) { - // Schedule an effect to clear this container at the start of the next commit. - // This handles the case of React rendering into a container with previous children. - // It's also safe to do for updates too, because current.child would only be null - // if the previous render was null (so the the container would already be empty). - workInProgress.flags |= Snapshot; - } - } + case HostPortal: + return updatePortalComponent(current, workInProgress, renderLanes); - updateHostContainer(current, workInProgress); - bubbleProperties(workInProgress); - return null; + case ForwardRef: { + var type = workInProgress.type; + var _unresolvedProps2 = workInProgress.pendingProps; + + var _resolvedProps2 = + workInProgress.elementType === type + ? _unresolvedProps2 + : resolveDefaultProps(type, _unresolvedProps2); + + return updateForwardRef( + current, + workInProgress, + type, + _resolvedProps2, + renderLanes + ); } - case HostComponent: { - popHostContext(workInProgress); - var rootContainerInstance = getRootHostContainer(); - var type = workInProgress.type; + case Fragment: + return updateFragment(current, workInProgress, renderLanes); - if (current !== null && workInProgress.stateNode != null) { - updateHostComponent$1( - current, - workInProgress, - type, - newProps, - rootContainerInstance - ); + case Mode: + return updateMode(current, workInProgress, renderLanes); - if (current.ref !== workInProgress.ref) { - markRef$1(workInProgress); - } - } else { - if (!newProps) { - if (!(workInProgress.stateNode !== null)) { - throw Error( - "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." - ); - } // This can happen when we abort work. + case Profiler: + return updateProfiler(current, workInProgress, renderLanes); - bubbleProperties(workInProgress); - return null; - } + case ContextProvider: + return updateContextProvider(current, workInProgress, renderLanes); - var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context - // "stack" as the parent. Then append children as we go in beginWork - // or completeWork depending on whether we want to add them top->down or - // bottom->up. Top->down is faster in IE11. + case ContextConsumer: + return updateContextConsumer(current, workInProgress, renderLanes); - var _wasHydrated = popHydrationState(); + case MemoComponent: { + var _type2 = workInProgress.type; + var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. - if (_wasHydrated) { - // TODO: Move this and createInstance step into the beginPhase - // to consolidate. - if (prepareToHydrateHostInstance()) { - // If changes to the hydrated node need to be applied at the - // commit-phase we mark this as such. - markUpdate(workInProgress); - } - } else { - var instance = createInstance( - type, - newProps, - rootContainerInstance, - currentHostContext, - workInProgress - ); - appendAllChildren(instance, workInProgress, false, false); - workInProgress.stateNode = instance; // 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. + var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); - if (finalizeInitialChildren(instance)) { - markUpdate(workInProgress); - } - } + { + if (workInProgress.type !== workInProgress.elementType) { + var outerPropTypes = _type2.propTypes; - if (workInProgress.ref !== null) { - // If there is a ref on a host node we need to schedule a callback - markRef$1(workInProgress); + if (outerPropTypes) { + checkPropTypes( + outerPropTypes, + _resolvedProps3, // Resolved for outer only + "prop", + getComponentNameFromType(_type2) + ); + } } } - bubbleProperties(workInProgress); - return null; + _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); + return updateMemoComponent( + current, + workInProgress, + _type2, + _resolvedProps3, + updateLanes, + renderLanes + ); + } + + case SimpleMemoComponent: { + return updateSimpleMemoComponent( + current, + workInProgress, + workInProgress.type, + workInProgress.pendingProps, + updateLanes, + renderLanes + ); } - case HostText: { - var newText = newProps; - - if (current && workInProgress.stateNode != null) { - var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need - // to schedule a side-effect to do the updates. + case IncompleteClassComponent: { + var _Component3 = workInProgress.type; + var _unresolvedProps4 = workInProgress.pendingProps; - updateHostText$1(current, workInProgress, oldText, newText); - } else { - if (typeof newText !== "string") { - if (!(workInProgress.stateNode !== null)) { - throw Error( - "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." - ); - } // This can happen when we abort work. - } + var _resolvedProps4 = + workInProgress.elementType === _Component3 + ? _unresolvedProps4 + : resolveDefaultProps(_Component3, _unresolvedProps4); - var _rootContainerInstance = getRootHostContainer(); + return mountIncompleteClassComponent( + current, + workInProgress, + _Component3, + _resolvedProps4, + renderLanes + ); + } - var _currentHostContext = getHostContext(); + case SuspenseListComponent: { + return updateSuspenseListComponent(current, workInProgress, renderLanes); + } - var _wasHydrated2 = popHydrationState(); + case ScopeComponent: { + break; + } - if (_wasHydrated2) { - if (prepareToHydrateHostTextInstance()) { - markUpdate(workInProgress); - } - } else { - workInProgress.stateNode = createTextInstance( - newText, - _rootContainerInstance, - _currentHostContext, - workInProgress - ); - } - } + case OffscreenComponent: { + return updateOffscreenComponent(current, workInProgress, renderLanes); + } - bubbleProperties(workInProgress); - return null; + case LegacyHiddenComponent: { + return updateLegacyHiddenComponent(current, workInProgress, renderLanes); } + } - case SuspenseComponent: { - popSuspenseContext(workInProgress); - var nextState = workInProgress.memoizedState; + { + throw Error( + "Unknown unit of work tag (" + + workInProgress.tag + + "). This error is likely caused by a bug in React. Please file an issue." + ); + } +} - if ((workInProgress.flags & DidCapture) !== NoFlags) { - // Something suspended. Re-render with the fallback children. - workInProgress.lanes = renderLanes; // Do not reset the effect list. +function markUpdate(workInProgress) { + // Tag the fiber with an update effect. This turns a Placement into + // a PlacementAndUpdate. + workInProgress.flags |= Update; +} - if ((workInProgress.mode & ProfileMode) !== NoMode) { - transferActualDuration(workInProgress); - } // Don't bubble properties in this case. +function markRef$1(workInProgress) { + workInProgress.flags |= Ref; +} - return workInProgress; - } +var appendAllChildren; +var updateHostContainer; +var updateHostComponent$1; +var updateHostText$1; - var nextDidTimeout = nextState !== null; - var prevDidTimeout = false; +{ + // Mutation mode + appendAllChildren = function( + parent, + workInProgress, + needsVisibilityToggle, + isHidden + ) { + // We only have the top Fiber that was created but we need recurse down its + // children to find all the terminal nodes. + var node = workInProgress.child; - if (current === null) { - if (workInProgress.memoizedProps.fallback !== undefined); - } else { - var prevState = current.memoizedState; - prevDidTimeout = prevState !== null; + while (node !== null) { + if (node.tag === HostComponent || node.tag === HostText) { + appendInitialChild(parent, node.stateNode); + } else if (node.tag === HostPortal); + else if (node.child !== null) { + node.child.return = node; + node = node.child; + continue; } - if (nextDidTimeout && !prevDidTimeout) { - // TODO: This will still suspend a synchronous tree if anything - // in the concurrent tree already suspended during this render. - // This is a known bug. - if ((workInProgress.mode & ConcurrentMode) !== NoMode) { - // TODO: Move this back to throwException because this is too late - // if this is a large tree which is common for initial loads. We - // don't know if we should restart a render or not until we get - // this marker, and this is too late. - // If this render already had a ping or lower pri updates, - // and this is the first time we know we're going to suspend we - // should be able to immediately restart from within throwException. - var hasInvisibleChildContext = - current === null && - workInProgress.memoizedProps.unstable_avoidThisFallback !== true; - - if ( - hasInvisibleChildContext || - hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext - ) - ) { - // If this was in an invisible tree or a new render, then showing - // this boundary is ok. - renderDidSuspend(); - } else { - // Otherwise, we're going to have to hide content so we should - // suspend for longer if possible. - renderDidSuspendDelayIfPossible(); - } - } + if (node === workInProgress) { + return; } - { - // TODO: Only schedule updates if these values are non equal, i.e. it changed. - if (nextDidTimeout || prevDidTimeout) { - // If this boundary just timed out, schedule an effect to attach a - // retry listener to the promise. This flag is also used to hide the - // primary children. In mutation mode, we also need the flag to - // *unhide* children that were previously hidden, so check if this - // is currently timed out, too. - workInProgress.flags |= Update; + while (node.sibling === null) { + if (node.return === null || node.return === workInProgress) { + return; } - } - - bubbleProperties(workInProgress); - - { - if ((workInProgress.mode & ProfileMode) !== NoMode) { - if (nextDidTimeout) { - // Don't count time spent in a timed out Suspense subtree as part of the base duration. - var _primaryChildFragment2 = workInProgress.child; - if (_primaryChildFragment2 !== null) { - // $FlowFixMe Flow doesn't support type casting in combination with the -= operator - workInProgress.treeBaseDuration -= - _primaryChildFragment2.treeBaseDuration; - } - } - } + node = node.return; } - return null; + node.sibling.return = node.return; + node = node.sibling; } + }; - case HostPortal: - popHostContainer(workInProgress); - updateHostContainer(current, workInProgress); + updateHostContainer = function(current, workInProgress) { + // Noop + }; - if (current === null) { - preparePortalMount(workInProgress.stateNode.containerInfo); - } + updateHostComponent$1 = function( + current, + workInProgress, + type, + newProps, + rootContainerInstance + ) { + // If we have an alternate, that means this is an update and we need to + // schedule a side-effect to do the updates. + var oldProps = current.memoizedProps; - bubbleProperties(workInProgress); - return null; + if (oldProps === newProps) { + // In mutation mode, this is sufficient for a bailout because + // we won't touch this node even if children changed. + return; + } // If we get updated because one of our children updated, we don't + // have newProps so we'll have to reuse them. + // TODO: Split the update API as separate for the props vs. children. + // Even better would be if children weren't special cased at all tho. - case ContextProvider: - // Pop provider fiber - var context = workInProgress.type._context; - popProvider(context, workInProgress); - bubbleProperties(workInProgress); - return null; + 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`. - case IncompleteClassComponent: { - // Same as class component case. I put it down here so that the tags are - // sequential to ensure this switch is compiled to a jump table. - var _Component = workInProgress.type; + var updatePayload = prepareUpdate(); // TODO: Type this specific to this type of component. - if (isContextProvider(_Component)) { - popContext(workInProgress); - } + workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there + // is a new ref we mark this as an update. All the work is done in commitWork. - bubbleProperties(workInProgress); - return null; + if (updatePayload) { + markUpdate(workInProgress); } + }; - case SuspenseListComponent: { - popSuspenseContext(workInProgress); - var renderState = workInProgress.memoizedState; + updateHostText$1 = function(current, workInProgress, oldText, newText) { + // If the text differs, mark it as an update. All the work in done in commitWork. + if (oldText !== newText) { + markUpdate(workInProgress); + } + }; +} + +function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { + switch (renderState.tailMode) { + case "hidden": { + // Any insertions at the end of the tail list after this point + // should be invisible. If there are already mounted boundaries + // anything before them are not considered for collapsing. + // Therefore we need to go through the whole tail to find if + // there are any. + var tailNode = renderState.tail; + var lastTailNode = null; - if (renderState === null) { - // We're running in the default, "independent" mode. - // We don't do anything in this mode. - bubbleProperties(workInProgress); - return null; - } + while (tailNode !== null) { + if (tailNode.alternate !== null) { + lastTailNode = tailNode; + } - var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; - var renderedTail = renderState.rendering; + tailNode = tailNode.sibling; + } // Next we're simply going to delete all insertions after the + // last rendered item. - if (renderedTail === null) { - // We just rendered the head. - if (!didSuspendAlready) { - // This is the first pass. We need to figure out if anything is still - // suspended in the rendered set. - // If new content unsuspended, but there's still some content that - // didn't. Then we need to do a second pass that forces everything - // to keep showing their fallbacks. - // We might be suspended if something in this render pass suspended, or - // something in the previous committed pass suspended. Otherwise, - // there's no chance so we can skip the expensive call to - // findFirstSuspended. - var cannotBeSuspended = - renderHasNotSuspendedYet() && - (current === null || (current.flags & DidCapture) === NoFlags); + if (lastTailNode === null) { + // All remaining items in the tail are insertions. + renderState.tail = null; + } else { + // Detach the insertion after the last node that was already + // inserted. + lastTailNode.sibling = null; + } - if (!cannotBeSuspended) { - var row = workInProgress.child; + break; + } - while (row !== null) { - var suspended = findFirstSuspended(row); + case "collapsed": { + // Any insertions at the end of the tail list after this point + // should be invisible. If there are already mounted boundaries + // anything before them are not considered for collapsing. + // Therefore we need to go through the whole tail to find if + // there are any. + var _tailNode = renderState.tail; + var _lastTailNode = null; - if (suspended !== null) { - didSuspendAlready = true; - workInProgress.flags |= DidCapture; - cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as - // part of the second pass. In that case nothing will subscribe to - // its thennables. Instead, we'll transfer its thennables to the - // SuspenseList so that it can retry if they resolve. - // There might be multiple of these in the list but since we're - // going to wait for all of them anyway, it doesn't really matter - // which ones gets to ping. In theory we could get clever and keep - // track of how many dependencies remain but it gets tricky because - // in the meantime, we can add/remove/change items and dependencies. - // We might bail out of the loop before finding any but that - // doesn't matter since that means that the other boundaries that - // we did find already has their listeners attached. + while (_tailNode !== null) { + if (_tailNode.alternate !== null) { + _lastTailNode = _tailNode; + } - var newThennables = suspended.updateQueue; + _tailNode = _tailNode.sibling; + } // Next we're simply going to delete all insertions after the + // last rendered item. - if (newThennables !== null) { - workInProgress.updateQueue = newThennables; - workInProgress.flags |= Update; - } // Rerender the whole list, but this time, we'll force fallbacks - // to stay in place. - // Reset the effect flags before doing the second pass since that's now invalid. - // Reset the child fibers to their original state. + if (_lastTailNode === null) { + // All remaining items in the tail are insertions. + if (!hasRenderedATailFallback && renderState.tail !== null) { + // We suspended during the head. We want to show at least one + // row at the tail. So we'll keep on and cut off the rest. + renderState.tail.sibling = null; + } else { + renderState.tail = null; + } + } else { + // Detach the insertion after the last node that was already + // inserted. + _lastTailNode.sibling = null; + } - workInProgress.subtreeFlags = NoFlags; - resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately - // rerender the children. + break; + } + } +} - pushSuspenseContext( - workInProgress, - setShallowSuspenseContext( - suspenseStackCursor.current, - ForceSuspenseFallback - ) - ); // Don't bubble properties in this case. +function bubbleProperties(completedWork) { + var didBailout = + completedWork.alternate !== null && + completedWork.alternate.child === completedWork.child; + var newChildLanes = NoLanes; + var subtreeFlags = NoFlags; - return workInProgress.child; - } + if (!didBailout) { + // Bubble up the earliest expiration time. + if ((completedWork.mode & ProfileMode) !== NoMode) { + // In profiling mode, resetChildExpirationTime is also used to reset + // profiler durations. + var actualDuration = completedWork.actualDuration; + var treeBaseDuration = completedWork.selfBaseDuration; + var child = completedWork.child; - row = row.sibling; - } - } + while (child !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(child.lanes, child.childLanes) + ); + subtreeFlags |= child.subtreeFlags; + subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will + // only be updated if work is done on the fiber (i.e. it doesn't bailout). + // When work is done, it should bubble to the parent's actualDuration. If + // the fiber has not been cloned though, (meaning no work was done), then + // this value will reflect the amount of time spent working on a previous + // render. In that case it should not bubble. We determine whether it was + // cloned by comparing the child pointer. - if (renderState.tail !== null && now() > getRenderTargetTime()) { - // We have already passed our CPU deadline but we still have rows - // left in the tail. We'll just give up further attempts to render - // the main content and only render fallbacks. - workInProgress.flags |= DidCapture; - didSuspendAlready = true; - cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this - // to get it started back up to attempt the next item. While in terms - // of priority this work has the same priority as this current render, - // it's not part of the same transition once the transition has - // committed. If it's sync, we still want to yield so that it can be - // painted. Conceptually, this is really the same as pinging. - // We can use any RetryLane even if it's the one currently rendering - // since we're leaving it behind on this node. + actualDuration += child.actualDuration; + treeBaseDuration += child.treeBaseDuration; + child = child.sibling; + } - workInProgress.lanes = SomeRetryLane; - } - } else { - cutOffTailIfNeeded(renderState, false); - } // Next we're going to render the tail. - } else { - // Append the rendered row to the child list. - if (!didSuspendAlready) { - var _suspended = findFirstSuspended(renderedTail); + completedWork.actualDuration = actualDuration; + completedWork.treeBaseDuration = treeBaseDuration; + } else { + var _child = completedWork.child; - if (_suspended !== null) { - workInProgress.flags |= DidCapture; - didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't - // get lost if this row ends up dropped during a second pass. + while (_child !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child.lanes, _child.childLanes) + ); + subtreeFlags |= _child.subtreeFlags; + subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code + // smell because it assumes the commit phase is never concurrent with + // the render phase. Will address during refactor to alternate model. - var _newThennables = _suspended.updateQueue; + _child.return = completedWork; + _child = _child.sibling; + } + } - if (_newThennables !== null) { - workInProgress.updateQueue = _newThennables; - workInProgress.flags |= Update; - } + completedWork.subtreeFlags |= subtreeFlags; + } else { + // Bubble up the earliest expiration time. + if ((completedWork.mode & ProfileMode) !== NoMode) { + // In profiling mode, resetChildExpirationTime is also used to reset + // profiler durations. + var _treeBaseDuration = completedWork.selfBaseDuration; + var _child2 = completedWork.child; - cutOffTailIfNeeded(renderState, true); // This might have been modified. + while (_child2 !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child2.lanes, _child2.childLanes) + ); // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. - if ( - renderState.tail === null && - renderState.tailMode === "hidden" && - !renderedTail.alternate && - !getIsHydrating() // We don't cut it if we're hydrating. - ) { - // We're done. - bubbleProperties(workInProgress); - return null; - } - } else if ( - // The time it took to render last row is greater than the remaining - // time we have to render. So rendering one more row would likely - // exceed it. - now() * 2 - renderState.renderingStartTime > - getRenderTargetTime() && - renderLanes !== OffscreenLane - ) { - // We have now passed our CPU deadline and we'll just give up further - // attempts to render the main content and only render fallbacks. - // The assumption is that this is usually faster. - workInProgress.flags |= DidCapture; - didSuspendAlready = true; - cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this - // to get it started back up to attempt the next item. While in terms - // of priority this work has the same priority as this current render, - // it's not part of the same transition once the transition has - // committed. If it's sync, we still want to yield so that it can be - // painted. Conceptually, this is really the same as pinging. - // We can use any RetryLane even if it's the one currently rendering - // since we're leaving it behind on this node. + subtreeFlags |= _child2.subtreeFlags & StaticMask; + subtreeFlags |= _child2.flags & StaticMask; + _treeBaseDuration += _child2.treeBaseDuration; + _child2 = _child2.sibling; + } - workInProgress.lanes = SomeRetryLane; - } - } + completedWork.treeBaseDuration = _treeBaseDuration; + } else { + var _child3 = completedWork.child; - if (renderState.isBackwards) { - // The effect list of the backwards tail will have been added - // to the end. This breaks the guarantee that life-cycles fire in - // sibling order but that isn't a strong guarantee promised by React. - // Especially since these might also just pop in during future commits. - // Append to the beginning of the list. - renderedTail.sibling = workInProgress.child; - workInProgress.child = renderedTail; - } else { - var previousSibling = renderState.last; + while (_child3 !== null) { + newChildLanes = mergeLanes( + newChildLanes, + mergeLanes(_child3.lanes, _child3.childLanes) + ); // "Static" flags share the lifetime of the fiber/hook they belong to, + // so we should bubble those up even during a bailout. All the other + // flags have a lifetime only of a single render + commit, so we should + // ignore them. - if (previousSibling !== null) { - previousSibling.sibling = renderedTail; - } else { - workInProgress.child = renderedTail; - } + subtreeFlags |= _child3.subtreeFlags & StaticMask; + subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code + // smell because it assumes the commit phase is never concurrent with + // the render phase. Will address during refactor to alternate model. - renderState.last = renderedTail; - } + _child3.return = completedWork; + _child3 = _child3.sibling; } + } - if (renderState.tail !== null) { - // We still have tail rows to render. - // Pop a row. - var next = renderState.tail; - renderState.rendering = next; - renderState.tail = next.sibling; - renderState.renderingStartTime = now(); - next.sibling = null; // Restore the context. - // TODO: We can probably just avoid popping it instead and only - // setting it the first time we go from not suspended to suspended. + completedWork.subtreeFlags |= subtreeFlags; + } - var suspenseContext = suspenseStackCursor.current; + completedWork.childLanes = newChildLanes; + return didBailout; +} - if (didSuspendAlready) { - suspenseContext = setShallowSuspenseContext( - suspenseContext, - ForceSuspenseFallback - ); - } else { - suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); - } +function completeWork(current, workInProgress, renderLanes) { + var newProps = workInProgress.pendingProps; - pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. - // Don't bubble properties in this case. + switch (workInProgress.tag) { + case IndeterminateComponent: + case LazyComponent: + case SimpleMemoComponent: + case FunctionComponent: + case ForwardRef: + case Fragment: + case Mode: + case Profiler: + case ContextConsumer: + case MemoComponent: + bubbleProperties(workInProgress); + return null; - return next; + case ClassComponent: { + var Component = workInProgress.type; + + if (isContextProvider(Component)) { + popContext(workInProgress); } bubbleProperties(workInProgress); return null; } - case ScopeComponent: { - break; - } + case HostRoot: { + var fiberRoot = workInProgress.stateNode; - case OffscreenComponent: - case LegacyHiddenComponent: { - popRenderLanes(workInProgress); - var _nextState = workInProgress.memoizedState; - var nextIsHidden = _nextState !== null; + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + resetWorkInProgressVersions(); - if (current !== null) { - var _prevState = current.memoizedState; - var prevIsHidden = _prevState !== null; + if (fiberRoot.pendingContext) { + fiberRoot.context = fiberRoot.pendingContext; + fiberRoot.pendingContext = null; + } - if ( - prevIsHidden !== nextIsHidden && - newProps.mode !== "unstable-defer-without-hiding" - ) { - workInProgress.flags |= Update; - } - } // Don't bubble properties for hidden children. + if (current === null || current.child === null) { + // If we hydrated, pop so that we can delete any remaining children + // that weren't hydrated. + var wasHydrated = popHydrationState(); - if ( - !nextIsHidden || - includesSomeLane(subtreeRenderLanes, OffscreenLane) || - (workInProgress.mode & ConcurrentMode) === NoMode - ) { - bubbleProperties(workInProgress); + if (wasHydrated) { + // If we hydrated, then we'll need to schedule an update for + // the commit side-effects on the root. + markUpdate(workInProgress); + } else if (!fiberRoot.hydrate) { + // Schedule an effect to clear this container at the start of the next commit. + // This handles the case of React rendering into a container with previous children. + // It's also safe to do for updates too, because current.child would only be null + // if the previous render was null (so the the container would already be empty). + workInProgress.flags |= Snapshot; + } } + updateHostContainer(current, workInProgress); + bubbleProperties(workInProgress); return null; } - } - { - throw Error( - "Unknown unit of work tag (" + - workInProgress.tag + - "). This error is likely caused by a bug in React. Please file an issue." - ); - } -} + case HostComponent: { + popHostContext(workInProgress); + var rootContainerInstance = getRootHostContainer(); + var type = workInProgress.type; -function unwindWork(workInProgress, renderLanes) { - switch (workInProgress.tag) { - case ClassComponent: { - var Component = workInProgress.type; + if (current !== null && workInProgress.stateNode != null) { + updateHostComponent$1( + current, + workInProgress, + type, + newProps, + rootContainerInstance + ); - if (isContextProvider(Component)) { - popContext(workInProgress); - } + if (current.ref !== workInProgress.ref) { + markRef$1(workInProgress); + } + } else { + if (!newProps) { + if (!(workInProgress.stateNode !== null)) { + throw Error( + "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." + ); + } // This can happen when we abort work. - var flags = workInProgress.flags; + bubbleProperties(workInProgress); + return null; + } - if (flags & ShouldCapture) { - workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; + var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context + // "stack" as the parent. Then append children as we go in beginWork + // or completeWork depending on whether we want to add them top->down or + // bottom->up. Top->down is faster in IE11. - if ((workInProgress.mode & ProfileMode) !== NoMode) { - transferActualDuration(workInProgress); + var _wasHydrated = popHydrationState(); + + if (_wasHydrated) { + // TODO: Move this and createInstance step into the beginPhase + // to consolidate. + if (prepareToHydrateHostInstance()) { + // If changes to the hydrated node need to be applied at the + // commit-phase we mark this as such. + markUpdate(workInProgress); + } + } else { + var instance = createInstance( + type, + newProps, + rootContainerInstance, + currentHostContext, + workInProgress + ); + appendAllChildren(instance, workInProgress, false, false); + workInProgress.stateNode = instance; // 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)) { + markUpdate(workInProgress); + } } - return workInProgress; + if (workInProgress.ref !== null) { + // If there is a ref on a host node we need to schedule a callback + markRef$1(workInProgress); + } } + bubbleProperties(workInProgress); return null; } - case HostRoot: { - popHostContainer(workInProgress); - popTopLevelContextObject(workInProgress); - resetWorkInProgressVersions(); - var _flags = workInProgress.flags; + case HostText: { + var newText = newProps; - if (!((_flags & DidCapture) === NoFlags)) { - throw Error( - "The root failed to unmount after an error. This is likely a bug in React. Please file an issue." - ); + if (current && workInProgress.stateNode != null) { + var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need + // to schedule a side-effect to do the updates. + + updateHostText$1(current, workInProgress, oldText, newText); + } else { + if (typeof newText !== "string") { + if (!(workInProgress.stateNode !== null)) { + throw Error( + "We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue." + ); + } // This can happen when we abort work. + } + + var _rootContainerInstance = getRootHostContainer(); + + var _currentHostContext = getHostContext(); + + var _wasHydrated2 = popHydrationState(); + + if (_wasHydrated2) { + if (prepareToHydrateHostTextInstance()) { + markUpdate(workInProgress); + } + } else { + workInProgress.stateNode = createTextInstance( + newText, + _rootContainerInstance, + _currentHostContext, + workInProgress + ); + } } - workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture; - return workInProgress; - } - - case HostComponent: { - // TODO: popHydrationState - popHostContext(workInProgress); + bubbleProperties(workInProgress); return null; } case SuspenseComponent: { popSuspenseContext(workInProgress); + var nextState = workInProgress.memoizedState; - var _flags2 = workInProgress.flags; - - if (_flags2 & ShouldCapture) { - workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary. + if ((workInProgress.flags & DidCapture) !== NoFlags) { + // Something suspended. Re-render with the fallback children. + workInProgress.lanes = renderLanes; // Do not reset the effect list. if ((workInProgress.mode & ProfileMode) !== NoMode) { transferActualDuration(workInProgress); - } + } // Don't bubble properties in this case. return workInProgress; } - return null; - } + var nextDidTimeout = nextState !== null; + var prevDidTimeout = false; - case SuspenseListComponent: { - popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been - // caught by a nested boundary. If not, it should bubble through. + if (current === null) { + if (workInProgress.memoizedProps.fallback !== undefined); + } else { + var prevState = current.memoizedState; + prevDidTimeout = prevState !== null; + } + + if (nextDidTimeout && !prevDidTimeout) { + // TODO: This will still suspend a synchronous tree if anything + // in the concurrent tree already suspended during this render. + // This is a known bug. + if ((workInProgress.mode & ConcurrentMode) !== NoMode) { + // TODO: Move this back to throwException because this is too late + // if this is a large tree which is common for initial loads. We + // don't know if we should restart a render or not until we get + // this marker, and this is too late. + // If this render already had a ping or lower pri updates, + // and this is the first time we know we're going to suspend we + // should be able to immediately restart from within throwException. + var hasInvisibleChildContext = + current === null && + workInProgress.memoizedProps.unstable_avoidThisFallback !== true; + + if ( + hasInvisibleChildContext || + hasSuspenseContext( + suspenseStackCursor.current, + InvisibleParentSuspenseContext + ) + ) { + // If this was in an invisible tree or a new render, then showing + // this boundary is ok. + renderDidSuspend(); + } else { + // Otherwise, we're going to have to hide content so we should + // suspend for longer if possible. + renderDidSuspendDelayIfPossible(); + } + } + } + + { + // TODO: Only schedule updates if these values are non equal, i.e. it changed. + if (nextDidTimeout || prevDidTimeout) { + // If this boundary just timed out, schedule an effect to attach a + // retry listener to the promise. This flag is also used to hide the + // primary children. In mutation mode, we also need the flag to + // *unhide* children that were previously hidden, so check if this + // is currently timed out, too. + workInProgress.flags |= Update; + } + } + + bubbleProperties(workInProgress); + + { + if ((workInProgress.mode & ProfileMode) !== NoMode) { + if (nextDidTimeout) { + // Don't count time spent in a timed out Suspense subtree as part of the base duration. + var _primaryChildFragment2 = workInProgress.child; + + if (_primaryChildFragment2 !== null) { + // $FlowFixMe Flow doesn't support type casting in combination with the -= operator + workInProgress.treeBaseDuration -= + _primaryChildFragment2.treeBaseDuration; + } + } + } + } return null; } case HostPortal: popHostContainer(workInProgress); + updateHostContainer(current, workInProgress); + + if (current === null) { + preparePortalMount(workInProgress.stateNode.containerInfo); + } + + bubbleProperties(workInProgress); return null; case ContextProvider: + // Pop provider fiber var context = workInProgress.type._context; popProvider(context, workInProgress); + bubbleProperties(workInProgress); return null; - case OffscreenComponent: - case LegacyHiddenComponent: - popRenderLanes(workInProgress); - - return null; - - case CacheComponent: - return null; - - default: - return null; - } -} - -function unwindInterruptedWork(interruptedWork, renderLanes) { - switch (interruptedWork.tag) { - case ClassComponent: { - var childContextTypes = interruptedWork.type.childContextTypes; + case IncompleteClassComponent: { + // Same as class component case. I put it down here so that the tags are + // sequential to ensure this switch is compiled to a jump table. + var _Component = workInProgress.type; - if (childContextTypes !== null && childContextTypes !== undefined) { - popContext(interruptedWork); + if (isContextProvider(_Component)) { + popContext(workInProgress); } - break; - } - - case HostRoot: { - popHostContainer(interruptedWork); - popTopLevelContextObject(interruptedWork); - resetWorkInProgressVersions(); - break; - } - - case HostComponent: { - popHostContext(interruptedWork); - break; + bubbleProperties(workInProgress); + return null; } - case HostPortal: - popHostContainer(interruptedWork); - break; - - case SuspenseComponent: - popSuspenseContext(interruptedWork); - break; - - case SuspenseListComponent: - popSuspenseContext(interruptedWork); - break; + case SuspenseListComponent: { + popSuspenseContext(workInProgress); + var renderState = workInProgress.memoizedState; - case ContextProvider: - var context = interruptedWork.type._context; - popProvider(context, interruptedWork); - break; + if (renderState === null) { + // We're running in the default, "independent" mode. + // We don't do anything in this mode. + bubbleProperties(workInProgress); + return null; + } - case OffscreenComponent: - case LegacyHiddenComponent: - popRenderLanes(interruptedWork); + var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; + var renderedTail = renderState.rendering; - break; - } -} + if (renderedTail === null) { + // We just rendered the head. + if (!didSuspendAlready) { + // This is the first pass. We need to figure out if anything is still + // suspended in the rendered set. + // If new content unsuspended, but there's still some content that + // didn't. Then we need to do a second pass that forces everything + // to keep showing their fallbacks. + // We might be suspended if something in this render pass suspended, or + // something in the previous committed pass suspended. Otherwise, + // there's no chance so we can skip the expensive call to + // findFirstSuspended. + var cannotBeSuspended = + renderHasNotSuspendedYet() && + (current === null || (current.flags & DidCapture) === NoFlags); -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: getStackByFiberInDevAndProd(source) - }; -} + if (!cannotBeSuspended) { + var row = workInProgress.child; -if ( - !( - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog === - "function" - ) -) { - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -} + while (row !== null) { + var suspended = findFirstSuspended(row); -function showErrorDialog(boundary, errorInfo) { - var capturedError = { - componentStack: errorInfo.stack !== null ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - boundary !== null && boundary.tag === ClassComponent - ? boundary.stateNode - : null - }; - return ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog( - capturedError - ); -} + if (suspended !== null) { + didSuspendAlready = true; + workInProgress.flags |= DidCapture; + cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as + // part of the second pass. In that case nothing will subscribe to + // its thennables. Instead, we'll transfer its thennables to the + // SuspenseList so that it can retry if they resolve. + // There might be multiple of these in the list but since we're + // going to wait for all of them anyway, it doesn't really matter + // which ones gets to ping. In theory we could get clever and keep + // track of how many dependencies remain but it gets tricky because + // in the meantime, we can add/remove/change items and dependencies. + // We might bail out of the loop before finding any but that + // doesn't matter since that means that the other boundaries that + // we did find already has their listeners attached. -function logCapturedError(boundary, errorInfo) { - try { - var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. - // This enables renderers like ReactNative to better manage redbox behavior. + var newThennables = suspended.updateQueue; - if (logError === false) { - return; - } + if (newThennables !== null) { + workInProgress.updateQueue = newThennables; + workInProgress.flags |= Update; + } // Rerender the whole list, but this time, we'll force fallbacks + // to stay in place. + // Reset the effect flags before doing the second pass since that's now invalid. + // Reset the child fibers to their original state. - var error = errorInfo.value; + workInProgress.subtreeFlags = NoFlags; + resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately + // rerender the children. - if (true) { - var source = errorInfo.source; - var stack = errorInfo.stack; - var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling - // `preventDefault()` in window `error` handler. - // We record this information as an expando on the error. + pushSuspenseContext( + workInProgress, + setShallowSuspenseContext( + suspenseStackCursor.current, + ForceSuspenseFallback + ) + ); // Don't bubble properties in this case. - if (error != null && error._suppressLogging) { - if (boundary.tag === ClassComponent) { - // The error is recoverable and was silenced. - // Ignore it and don't print the stack addendum. - // This is handy for testing error boundaries without noise. - return; - } // The error is fatal. Since the silencing might have - // been accidental, we'll surface it anyway. - // However, the browser would have silenced the original error - // so we'll print it first, and then print the stack addendum. + return workInProgress.child; + } - console["error"](error); // Don't transform to our wrapper - // For a more detailed description of this block, see: - // https://github.com/facebook/react/pull/13384 - } + row = row.sibling; + } + } - var componentName = source ? getComponentNameFromFiber(source) : null; - var componentNameMessage = componentName - ? "The above error occurred in the <" + componentName + "> component:" - : "The above error occurred in one of your React components:"; - var errorBoundaryMessage; + if (renderState.tail !== null && now() > getRenderTargetTime()) { + // We have already passed our CPU deadline but we still have rows + // left in the tail. We'll just give up further attempts to render + // the main content and only render fallbacks. + workInProgress.flags |= DidCapture; + didSuspendAlready = true; + cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this + // to get it started back up to attempt the next item. While in terms + // of priority this work has the same priority as this current render, + // it's not part of the same transition once the transition has + // committed. If it's sync, we still want to yield so that it can be + // painted. Conceptually, this is really the same as pinging. + // We can use any RetryLane even if it's the one currently rendering + // since we're leaving it behind on this node. - if (boundary.tag === HostRoot) { - errorBoundaryMessage = - "Consider adding an error boundary to your tree to customize error handling behavior.\n" + - "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries."; + workInProgress.lanes = SomeRetryLane; + } + } else { + cutOffTailIfNeeded(renderState, false); + } // Next we're going to render the tail. } else { - var errorBoundaryName = - getComponentNameFromFiber(boundary) || "Anonymous"; - errorBoundaryMessage = - "React will try to recreate this component tree from scratch " + - ("using the error boundary you provided, " + errorBoundaryName + "."); - } - - var combinedMessage = - componentNameMessage + - "\n" + - 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. + // Append the rendered row to the child list. + if (!didSuspendAlready) { + var _suspended = findFirstSuspended(renderedTail); - console["error"](combinedMessage); // Don't transform to our wrapper - } else { - // In production, we print the error directly. - // This will include the message, the JS stack, and anything the browser wants to show. - // We pass the error object instead of custom message so that the browser displays the error natively. - console["error"](error); // Don't transform to our wrapper - } - } catch (e) { - // This method must not throw, or React internal state will get messed up. - // If console.error is overridden, or logCapturedError() shows a dialog that throws, - // we want to report this error outside of the normal stack as a last resort. - // https://github.com/facebook/react/issues/13188 - setTimeout(function() { - throw e; - }); - } -} + if (_suspended !== null) { + workInProgress.flags |= DidCapture; + didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't + // get lost if this row ends up dropped during a second pass. -var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; + var _newThennables = _suspended.updateQueue; -function createRootErrorUpdate(fiber, errorInfo, lane) { - var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. + if (_newThennables !== null) { + workInProgress.updateQueue = _newThennables; + workInProgress.flags |= Update; + } - update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property - // being called "element". + cutOffTailIfNeeded(renderState, true); // This might have been modified. - update.payload = { - element: null - }; - var error = errorInfo.value; + if ( + renderState.tail === null && + renderState.tailMode === "hidden" && + !renderedTail.alternate && + !getIsHydrating() // We don't cut it if we're hydrating. + ) { + // We're done. + bubbleProperties(workInProgress); + return null; + } + } else if ( + // The time it took to render last row is greater than the remaining + // time we have to render. So rendering one more row would likely + // exceed it. + now() * 2 - renderState.renderingStartTime > + getRenderTargetTime() && + renderLanes !== OffscreenLane + ) { + // We have now passed our CPU deadline and we'll just give up further + // attempts to render the main content and only render fallbacks. + // The assumption is that this is usually faster. + workInProgress.flags |= DidCapture; + didSuspendAlready = true; + cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this + // to get it started back up to attempt the next item. While in terms + // of priority this work has the same priority as this current render, + // it's not part of the same transition once the transition has + // committed. If it's sync, we still want to yield so that it can be + // painted. Conceptually, this is really the same as pinging. + // We can use any RetryLane even if it's the one currently rendering + // since we're leaving it behind on this node. - update.callback = function() { - onUncaughtError(error); - logCapturedError(fiber, errorInfo); - }; + workInProgress.lanes = SomeRetryLane; + } + } - return update; -} + if (renderState.isBackwards) { + // The effect list of the backwards tail will have been added + // to the end. This breaks the guarantee that life-cycles fire in + // sibling order but that isn't a strong guarantee promised by React. + // Especially since these might also just pop in during future commits. + // Append to the beginning of the list. + renderedTail.sibling = workInProgress.child; + workInProgress.child = renderedTail; + } else { + var previousSibling = renderState.last; -function createClassErrorUpdate(fiber, errorInfo, lane) { - var update = createUpdate(NoTimestamp, lane); - update.tag = CaptureUpdate; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if (previousSibling !== null) { + previousSibling.sibling = renderedTail; + } else { + workInProgress.child = renderedTail; + } - if (typeof getDerivedStateFromError === "function") { - var error$1 = errorInfo.value; + renderState.last = renderedTail; + } + } - update.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error$1); - }; - } + if (renderState.tail !== null) { + // We still have tail rows to render. + // Pop a row. + var next = renderState.tail; + renderState.rendering = next; + renderState.tail = next.sibling; + renderState.renderingStartTime = now(); + next.sibling = null; // Restore the context. + // TODO: We can probably just avoid popping it instead and only + // setting it the first time we go from not suspended to suspended. - var inst = fiber.stateNode; + var suspenseContext = suspenseStackCursor.current; - if (inst !== null && typeof inst.componentDidCatch === "function") { - update.callback = function callback() { - { - markFailedErrorBoundaryForHotReloading(fiber); - } + if (didSuspendAlready) { + suspenseContext = setShallowSuspenseContext( + suspenseContext, + ForceSuspenseFallback + ); + } else { + suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); + } - if (typeof getDerivedStateFromError !== "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 getDerivedStateFromError is - // not defined. - markLegacyErrorBoundaryAsFailed(this); // Only log here if componentDidCatch is the only error boundary method defined + pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. + // Don't bubble properties in this case. - logCapturedError(fiber, errorInfo); + return next; } - var error$1 = errorInfo.value; - var stack = errorInfo.stack; - this.componentDidCatch(error$1, { - componentStack: stack !== null ? stack : "" - }); + bubbleProperties(workInProgress); + return null; + } - { - if (typeof getDerivedStateFromError !== "function") { - // If componentDidCatch is the only error boundary method defined, - // then it needs to call setState to recover from errors. - // If no state update is scheduled then the boundary will swallow the error. - if (!includesSomeLane(fiber.lanes, SyncLane)) { - error( - "%s: Error boundaries should implement getDerivedStateFromError(). " + - "In that method, return a state update to display an error message or fallback UI.", - getComponentNameFromFiber(fiber) || "Unknown" - ); - } - } - } - }; - } else { - update.callback = function() { - markFailedErrorBoundaryForHotReloading(fiber); - }; - } + case ScopeComponent: { + break; + } - return update; -} + case OffscreenComponent: + case LegacyHiddenComponent: { + popRenderLanes(workInProgress); + var _nextState = workInProgress.memoizedState; + var nextIsHidden = _nextState !== null; -function attachPingListener(root, wakeable, lanes) { - // Attach a listener to the promise to "ping" the root and retry. But only if - // one does not already exist for the lanes we're currently rendering (which - // acts like a "thread ID" here). - var pingCache = root.pingCache; - var threadIDs; + if (current !== null) { + var _prevState = current.memoizedState; + var prevIsHidden = _prevState !== null; - if (pingCache === null) { - pingCache = root.pingCache = new PossiblyWeakMap$1(); - threadIDs = new Set(); - pingCache.set(wakeable, threadIDs); - } else { - threadIDs = pingCache.get(wakeable); + if ( + prevIsHidden !== nextIsHidden && + newProps.mode !== "unstable-defer-without-hiding" + ) { + workInProgress.flags |= Update; + } + } // Don't bubble properties for hidden children. - if (threadIDs === undefined) { - threadIDs = new Set(); - pingCache.set(wakeable, threadIDs); + if ( + !nextIsHidden || + includesSomeLane(subtreeRenderLanes, OffscreenLane) || + (workInProgress.mode & ConcurrentMode) === NoMode + ) { + bubbleProperties(workInProgress); + } + + return null; } } - if (!threadIDs.has(lanes)) { - // Memoize using the thread ID to prevent redundant listeners. - threadIDs.add(lanes); - var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); - - wakeable.then(ping, ping); + { + throw Error( + "Unknown unit of work tag (" + + workInProgress.tag + + "). This error is likely caused by a bug in React. Please file an issue." + ); } } -function throwException( - root, - returnFiber, - sourceFiber, - value, - rootRenderLanes -) { - // The source fiber did not complete. - sourceFiber.flags |= Incomplete; +function unwindWork(workInProgress, renderLanes) { + switch (workInProgress.tag) { + case ClassComponent: { + var Component = workInProgress.type; - if ( - value !== null && - typeof value === "object" && - typeof value.then === "function" - ) { - var wakeable = value; - // A legacy mode Suspense quirk, only relevant to hook components. + if (isContextProvider(Component)) { + popContext(workInProgress); + } - var tag = sourceFiber.tag; + var flags = workInProgress.flags; - if ( - (sourceFiber.mode & ConcurrentMode) === NoMode && - (tag === FunctionComponent || - tag === ForwardRef || - tag === SimpleMemoComponent) - ) { - var currentSource = sourceFiber.alternate; + if (flags & ShouldCapture) { + workInProgress.flags = (flags & ~ShouldCapture) | DidCapture; - if (currentSource) { - sourceFiber.updateQueue = currentSource.updateQueue; - sourceFiber.memoizedState = currentSource.memoizedState; - sourceFiber.lanes = currentSource.lanes; - } else { - sourceFiber.updateQueue = null; - sourceFiber.memoizedState = null; + if ((workInProgress.mode & ProfileMode) !== NoMode) { + transferActualDuration(workInProgress); + } + + return workInProgress; } + + return null; } - var hasInvisibleParentBoundary = hasSuspenseContext( - suspenseStackCursor.current, - InvisibleParentSuspenseContext - ); // Schedule the nearest Suspense to re-render the timed out view. + case HostRoot: { + popHostContainer(workInProgress); + popTopLevelContextObject(workInProgress); + resetWorkInProgressVersions(); + var _flags = workInProgress.flags; - var _workInProgress = returnFiber; + if (!((_flags & DidCapture) === NoFlags)) { + throw Error( + "The root failed to unmount after an error. This is likely a bug in React. Please file an issue." + ); + } - do { - if ( - _workInProgress.tag === SuspenseComponent && - shouldCaptureSuspense(_workInProgress, hasInvisibleParentBoundary) - ) { - // Found the nearest boundary. - // Stash the promise on the boundary fiber. If the boundary times out, we'll - // attach another listener to flip the boundary back to its normal state. - var wakeables = _workInProgress.updateQueue; + workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture; + return workInProgress; + } - if (wakeables === null) { - var updateQueue = new Set(); - updateQueue.add(wakeable); - _workInProgress.updateQueue = updateQueue; - } else { - wakeables.add(wakeable); - } // If the boundary is in legacy mode, we should *not* - // suspend the commit. Pretend as if the suspended component rendered - // null and keep rendering. In the commit phase, we'll schedule a - // subsequent synchronous update to re-render the Suspense. - // - // Note: It doesn't matter whether the component that suspended was - // inside a concurrent mode tree. If the Suspense is outside of it, we - // should *not* suspend the commit. - // - // If the suspense boundary suspended itself suspended, we don't have to - // do this trick because nothing was partially started. We can just - // directly do a second pass over the fallback in this render and - // pretend we meant to render that directly. + case HostComponent: { + // TODO: popHydrationState + popHostContext(workInProgress); + return null; + } - if ( - (_workInProgress.mode & ConcurrentMode) === NoMode && - _workInProgress !== returnFiber - ) { - _workInProgress.flags |= DidCapture; - sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. - // But we shouldn't call any lifecycle methods or callbacks. Remove - // all lifecycle effect tags. + case SuspenseComponent: { + popSuspenseContext(workInProgress); - sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); + var _flags2 = workInProgress.flags; - if (sourceFiber.tag === ClassComponent) { - var _currentSourceFiber = sourceFiber.alternate; + if (_flags2 & ShouldCapture) { + workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary. - if (_currentSourceFiber === null) { - // This is a new mount. Change the tag so it's not mistaken for a - // completed class component. For example, we should not call - // componentWillUnmount if it is deleted. - sourceFiber.tag = IncompleteClassComponent; - } else { - // When we try rendering again, we should not reuse the current fiber, - // since it's known to be in an inconsistent state. Use a force update to - // prevent a bail out. - var update = createUpdate(NoTimestamp, SyncLane); - update.tag = ForceUpdate; - enqueueUpdate(sourceFiber, update); - } - } // The source fiber did not complete. Mark it with Sync priority to - // indicate that it still has pending work. + if ((workInProgress.mode & ProfileMode) !== NoMode) { + transferActualDuration(workInProgress); + } - sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); // Exit without suspending. + return workInProgress; + } - return; - } // Confirmed that the boundary is in a concurrent mode tree. Continue - // with the normal suspend path. - // - // After this we'll use a set of heuristics to determine whether this - // render pass will run to completion or restart or "suspend" the commit. - // The actual logic for this is spread out in different places. - // - // This first principle is that if we're going to suspend when we complete - // a root, then we should also restart if we get an update or ping that - // might unsuspend it, and vice versa. The only reason to suspend is - // because you think you might want to restart before committing. However, - // it doesn't make sense to restart only while in the period we're suspended. - // - // Restarting too aggressively is also not good because it starves out any - // intermediate loading state. So we use heuristics to determine when. - // Suspense Heuristics - // - // If nothing threw a Promise or all the same fallbacks are already showing, - // then don't suspend/restart. - // - // If this is an initial render of a new tree of Suspense boundaries and - // those trigger a fallback, then don't suspend/restart. We want to ensure - // that we can show the initial loading state as quickly as possible. - // - // If we hit a "Delayed" case, such as when we'd switch from content back into - // a fallback, then we should always suspend/restart. Transitions apply - // to this case. If none is defined, JND is used instead. - // - // If we're already showing a fallback and it gets "retried", allowing us to show - // another level, but there's still an inner boundary that would show a fallback, - // then we suspend/restart for 500ms since the last time we showed a fallback - // anywhere in the tree. This effectively throttles progressive loading into a - // consistent train of commits. This also gives us an opportunity to restart to - // get to the completed state slightly earlier. - // - // If there's ambiguity due to batching it's resolved in preference of: - // 1) "delayed", 2) "initial render", 3) "retry". - // - // We want to ensure that a "busy" state doesn't get force committed. We want to - // ensure that new initial loading states can commit as soon as possible. + return null; + } - attachPingListener(root, wakeable, rootRenderLanes); - _workInProgress.flags |= ShouldCapture; - _workInProgress.lanes = rootRenderLanes; - return; - } // This boundary already captured during this render. Continue to the next - // boundary. + case SuspenseListComponent: { + popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been + // caught by a nested boundary. If not, it should bubble through. - _workInProgress = _workInProgress.return; - } while (_workInProgress !== null); // No boundary was found. Fallthrough to error mode. - // TODO: Use invariant so the message is stripped in prod? + return null; + } + + case HostPortal: + popHostContainer(workInProgress); + return null; + + case ContextProvider: + var context = workInProgress.type._context; + popProvider(context, workInProgress); + return null; + + case OffscreenComponent: + case LegacyHiddenComponent: + popRenderLanes(workInProgress); - value = new Error( - (getComponentNameFromFiber(sourceFiber) || "A React component") + - " suspended while rendering, but no fallback UI was specified.\n" + - "\n" + - "Add a component higher in the tree to " + - "provide a loading indicator or placeholder to display." - ); - } // We didn't find a boundary that could handle this type of exception. Start - // over and traverse parent path again, this time treating the exception - // as an error. + return null; - renderDidError(); - value = createCapturedValue(value, sourceFiber); - var workInProgress = returnFiber; + case CacheComponent: + return null; - do { - switch (workInProgress.tag) { - case HostRoot: { - var _errorInfo = value; - workInProgress.flags |= ShouldCapture; - var lane = pickArbitraryLane(rootRenderLanes); - workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); + default: + return null; + } +} - var _update = createRootErrorUpdate(workInProgress, _errorInfo, lane); +function unwindInterruptedWork(interruptedWork, renderLanes) { + switch (interruptedWork.tag) { + case ClassComponent: { + var childContextTypes = interruptedWork.type.childContextTypes; - enqueueCapturedUpdate(workInProgress, _update); - return; + if (childContextTypes !== null && childContextTypes !== undefined) { + popContext(interruptedWork); } - case ClassComponent: - // Capture and retry - var errorInfo = value; - var ctor = workInProgress.type; - var instance = workInProgress.stateNode; + break; + } - if ( - (workInProgress.flags & DidCapture) === NoFlags && - (typeof ctor.getDerivedStateFromError === "function" || - (instance !== null && - typeof instance.componentDidCatch === "function" && - !isAlreadyFailedLegacyErrorBoundary(instance))) - ) { - workInProgress.flags |= ShouldCapture; + case HostRoot: { + popHostContainer(interruptedWork); + popTopLevelContextObject(interruptedWork); + resetWorkInProgressVersions(); + break; + } - var _lane = pickArbitraryLane(rootRenderLanes); + case HostComponent: { + popHostContext(interruptedWork); + break; + } - workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state + case HostPortal: + popHostContainer(interruptedWork); + break; - var _update2 = createClassErrorUpdate( - workInProgress, - errorInfo, - _lane - ); + case SuspenseComponent: + popSuspenseContext(interruptedWork); + break; - enqueueCapturedUpdate(workInProgress, _update2); - return; - } + case SuspenseListComponent: + popSuspenseContext(interruptedWork); + break; - break; - } + case ContextProvider: + var context = interruptedWork.type._context; + popProvider(context, interruptedWork); + break; - workInProgress = workInProgress.return; - } while (workInProgress !== null); + case OffscreenComponent: + case LegacyHiddenComponent: + popRenderLanes(interruptedWork); + + break; + } } var didWarnAboutUndefinedSnapshotBeforeUpdate = null; @@ -16568,11 +16785,21 @@ var didWarnAboutUndefinedSnapshotBeforeUpdate = null; var PossiblyWeakSet = typeof WeakSet === "function" ? WeakSet : Set; var nextEffect = null; // Used for Profiling builds to track updaters. +var inProgressLanes = null; +var inProgressRoot = null; + var callComponentWillUnmountWithTimer = function(current, instance) { instance.props = current.memoizedProps; instance.state = current.memoizedState; - { + if (current.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentWillUnmount(); + } finally { + recordLayoutEffectDuration(current); + } + } else { instance.componentWillUnmount(); } }; // Capture errors so they don't interrupt mounting. @@ -16604,7 +16831,11 @@ function safelyDetachRef(current, nearestMountedAncestor) { if (ref !== null) { if (typeof ref === "function") { { - { + if (current.mode & ProfileMode) { + startLayoutEffectTimer(); + invokeGuardedCallback(null, ref, null, null); + recordLayoutEffectDuration(current); + } else { invokeGuardedCallback(null, ref, null, null); } @@ -16916,6 +17147,58 @@ function commitHookEffectListMount(tag, finishedWork) { } } +function commitPassiveEffectDurations(finishedRoot, finishedWork) { + { + // Only Profilers with work in their subtree will have an Update effect scheduled. + if ((finishedWork.flags & Update) !== NoFlags) { + switch (finishedWork.tag) { + case Profiler: { + var passiveEffectDuration = + finishedWork.stateNode.passiveEffectDuration; + var _finishedWork$memoize = finishedWork.memoizedProps, + id = _finishedWork$memoize.id, + onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase. + // It does not get reset until the start of the next commit phase. + + var commitTime = getCommitTime(); + var phase = finishedWork.alternate === null ? "mount" : "update"; + + { + if (isCurrentUpdateNested()) { + phase = "nested-update"; + } + } + + if (typeof onPostCommit === "function") { + onPostCommit(id, phase, passiveEffectDuration, commitTime); + } // Bubble times to the next nearest ancestor Profiler. + // After we process that Profiler, we'll bubble further up. + + var parentFiber = finishedWork.return; + + outer: while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.passiveEffectDuration += passiveEffectDuration; + break outer; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.passiveEffectDuration += passiveEffectDuration; + break outer; + } + + parentFiber = parentFiber.return; + } + + break; + } + } + } + } +} + function commitLayoutEffectOnFiber( finishedRoot, current, @@ -16931,7 +17214,14 @@ function commitLayoutEffectOnFiber( // This is done to prevent sibling component effects from interfering with each other, // e.g. a destroy function in one component should never override a ref set // by a create function in another component during the same commit. - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + commitHookEffectListMount(Layout | HasEffect, finishedWork); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { commitHookEffectListMount(Layout | HasEffect, finishedWork); } @@ -16975,7 +17265,14 @@ function commitLayoutEffectOnFiber( } } - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentDidMount(); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { instance.componentDidMount(); } } else { @@ -17016,7 +17313,18 @@ function commitLayoutEffectOnFiber( } } - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate + ); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { instance.componentDidUpdate( prevProps, prevState, @@ -17126,6 +17434,12 @@ function commitLayoutEffectOnFiber( var commitTime = getCommitTime(); var phase = current === null ? "mount" : "update"; + { + if (isCurrentUpdateNested()) { + phase = "nested-update"; + } + } + if (typeof onRender === "function") { onRender( finishedWork.memoizedProps.id, @@ -17136,6 +17450,40 @@ function commitLayoutEffectOnFiber( commitTime ); } + + { + if (typeof onCommit === "function") { + onCommit( + finishedWork.memoizedProps.id, + phase, + effectDuration, + commitTime + ); + } // Schedule a passive effect for this Profiler to call onPostCommit hooks. + // This effect should be scheduled even if there is no onPostCommit callback for this Profiler, + // because the effect is also where times bubble to parent Profilers. + + enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor. + // Do not reset these values until the next render so DevTools has a chance to read them first. + + var parentFiber = finishedWork.return; + + outer: while (parentFiber !== null) { + switch (parentFiber.tag) { + case HostRoot: + var root = parentFiber.stateNode; + root.effectDuration += effectDuration; + break outer; + + case Profiler: + var parentStateNode = parentFiber.stateNode; + parentStateNode.effectDuration += effectDuration; + break outer; + } + + parentFiber = parentFiber.return; + } + } } break; @@ -17257,7 +17605,14 @@ function commitAttachRef(finishedWork) { } // Moved outside to ensure DCE works with this flag if (typeof ref === "function") { - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + ref(instanceToUse); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { ref(instanceToUse); } } else { @@ -17281,7 +17636,14 @@ function commitDetachRef(current) { if (currentRef !== null) { if (typeof currentRef === "function") { - { + if (current.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + currentRef(null); + } finally { + recordLayoutEffectDuration(current); + } + } else { currentRef(null); } } else { @@ -17316,7 +17678,11 @@ function commitUnmount(finishedRoot, current, nearestMountedAncestor) { if (destroy !== undefined) { if ((tag & Layout) !== NoFlags$1) { - { + if (current.mode & ProfileMode) { + startLayoutEffectTimer(); + safelyCallDestroy(current, nearestMountedAncestor, destroy); + recordLayoutEffectDuration(current); + } else { safelyCallDestroy(current, nearestMountedAncestor, destroy); } } @@ -17761,7 +18127,18 @@ function commitWork(current, finishedWork) { // This prevents sibling component effects from interfering with each other, // e.g. a destroy function in one component should never override a ref set // by a create function in another component during the same commit. - { + if (finishedWork.mode & ProfileMode) { + try { + startLayoutEffectTimer(); + commitHookEffectListUnmount( + Layout | HasEffect, + finishedWork, + finishedWork.return + ); + } finally { + recordLayoutEffectDuration(finishedWork); + } + } else { commitHookEffectListUnmount( Layout | HasEffect, finishedWork, @@ -17902,6 +18279,19 @@ function attachSuspenseRetryListeners(finishedWork) { if (!retryCache.has(wakeable)) { retryCache.add(wakeable); + { + if (isDevToolsPresent) { + if (inProgressLanes !== null && inProgressRoot !== null) { + // If we have pending work still, associate the original updaters with it. + restorePendingUpdaters(inProgressRoot, inProgressLanes); + } else { + throw Error( + "Expected finished root and lanes to be set. This is a bug in React." + ); + } + } + } + wakeable.then(retry, retry); } }); @@ -17928,8 +18318,12 @@ function commitResetTextContent(current) { } function commitMutationEffects(root, firstChild, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; nextEffect = firstChild; commitMutationEffects_begin(root); + inProgressLanes = null; + inProgressRoot = null; } function commitMutationEffects_begin(root) { @@ -18070,8 +18464,12 @@ function commitMutationEffectsOnFiber(finishedWork, root) { } function commitLayoutEffects(finishedWork, root, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; nextEffect = finishedWork; commitLayoutEffects_begin(finishedWork, root, committedLanes); + inProgressLanes = null; + inProgressRoot = null; } function commitLayoutEffects_begin(subtreeRoot, root, committedLanes) { @@ -18204,7 +18602,15 @@ function commitPassiveMountOnFiber(finishedRoot, finishedWork) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (finishedWork.mode & ProfileMode) { + startPassiveEffectTimer(); + + try { + commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); + } finally { + recordPassiveEffectDuration(finishedWork); + } + } else { commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); } @@ -18305,7 +18711,15 @@ function commitPassiveUnmountOnFiber(finishedWork) { case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (finishedWork.mode & ProfileMode) { + startPassiveEffectTimer(); + commitHookEffectListUnmount( + Passive$1 | HasEffect, + finishedWork, + finishedWork.return + ); + recordPassiveEffectDuration(finishedWork); + } else { commitHookEffectListUnmount( Passive$1 | HasEffect, finishedWork, @@ -18379,7 +18793,11 @@ function commitPassiveUnmountInsideDeletedTreeOnFiber( case FunctionComponent: case ForwardRef: case SimpleMemoComponent: { - { + if (current.mode & ProfileMode) { + startPassiveEffectTimer(); + commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); + recordPassiveEffectDuration(current); + } else { commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); } @@ -18507,6 +18925,7 @@ var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfi var rootDoesHavePassiveEffects = false; var rootWithPendingPassiveEffects = null; var pendingPassiveEffectsLanes = NoLanes; +var pendingPassiveProfilerEffects = []; // Use these to prevent an infinite loop of nested updates var NESTED_UPDATE_LIMIT = 50; var nestedUpdateCount = 0; @@ -18544,6 +18963,20 @@ function requestUpdateLane(fiber) { if ((mode & ConcurrentMode) === NoMode) { return SyncLane; + } else if ( + (executionContext & RenderContext) !== NoContext && + workInProgressRootRenderLanes !== NoLanes + ) { + // This is a render phase update. These are not officially supported. The + // old behavior is to give this the same "thread" (lanes) as + // whatever is currently rendering. So if you call `setState` on a component + // that happens later in the same render, it will flush. Ideally, we want to + // remove the special case and treat them as if they came from an + // interleaved event. Regardless, this pattern is not officially supported. + // This behavior is only a fallback. The flag only exists until we can roll + // out the setState warning, since existing code might accidentally rely on + // the current behavior. + return pickArbitraryLane(workInProgressRootRenderLanes); } var isTransition = requestCurrentTransition() !== NoTransition; @@ -18573,7 +19006,7 @@ function requestUpdateLane(fiber) { if (updateLane !== NoLane) { return updateLane; - } // This update originated outside React. Ask the host environement for an + } // This update originated outside React. Ask the host environment for an // appropriate priority, based on the type of event. // // The opaque type returned by the host config is internally a lane, so we can @@ -18608,6 +19041,12 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { return null; } + { + if (isDevToolsPresent) { + addFiberToLanesMap(root, fiber, lane); + } + } // Mark that the root has a pending update. + markRootUpdated(root, lane, eventTime); if (root === workInProgressRoot) { @@ -18616,7 +19055,7 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render // phase update. In that case, we don't treat render phase updates as if // they were interleaved, for backwards compat reasons. - { + if ((executionContext & RenderContext) === NoContext) { workInProgressRootUpdatedLanes = mergeLanes( workInProgressRootUpdatedLanes, lane @@ -18728,7 +19167,7 @@ function isInterleavedUpdate(fiber, lane) { // then don't treat this as an interleaved update. This pattern is // accompanied by a warning but we haven't fully deprecated it yet. We can // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled. - deferRenderPhaseUpdateToNextBatch + (executionContext & RenderContext) === NoContext ); } // Use this function to schedule a task for a root. There's only one task per // root; if a task was already scheduled, we'll check to make sure the priority @@ -18839,6 +19278,9 @@ function ensureRootIsScheduled(root, currentTime) { // goes through Scheduler. function performConcurrentWorkOnRoot(root, didTimeout) { + { + resetNestedUpdateFlag(); + } // Since we know we're in a React event, we can clear the current // event time. The next update will compute a new event time. currentEventTime = NoTimestamp; @@ -18861,7 +19303,7 @@ function performConcurrentWorkOnRoot(root, didTimeout) { // there's a new task, or that there's no remaining work on this root. return null; } - } // Determine the next expiration time to work on, using the fields stored + } // Determine the next lanes to work on, using the fields stored // on the root. var lanes = getNextLanes( @@ -19066,6 +19508,10 @@ function markRootSuspended$1(root, suspendedLanes) { // through Scheduler function performSyncWorkOnRoot(root) { + { + syncNestedUpdateFlag(); + } + if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) { throw Error("Should not already be working."); } @@ -19366,6 +19812,22 @@ function renderRootSync(root, lanes) { // and prepare a fresh one. Otherwise we'll continue where we left off. if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + + if (memoizedUpdaters.size > 0) { + restorePendingUpdaters(root, workInProgressRootRenderLanes); + memoizedUpdaters.clear(); + } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. + // If we bailout on this work, we'll move them back (like above). + // It's important to move them now in case the work spawns more work at the same priority with different updaters. + // That way we can keep the current update and future updates separate. + + movePendingFibersToMemoized(root, lanes); + } + } + prepareFreshStack(root, lanes); } @@ -19412,6 +19874,22 @@ function renderRootConcurrent(root, lanes) { // and prepare a fresh one. Otherwise we'll continue where we left off. if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + + if (memoizedUpdaters.size > 0) { + restorePendingUpdaters(root, workInProgressRootRenderLanes); + memoizedUpdaters.clear(); + } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. + // If we bailout on this work, we'll move them back (like above). + // It's important to move them now in case the work spawns more work at the same priority with different updaters. + // That way we can keep the current update and future updates separate. + + movePendingFibersToMemoized(root, lanes); + } + } + resetRenderTimer(); prepareFreshStack(root, lanes); } @@ -19512,7 +19990,7 @@ function completeUnitOfWork(unitOfWork) { // 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(completedWork); // Because this fiber did not complete, don't reset its expiration time. + var _next = unwindWork(completedWork); // Because this fiber did not complete, don't reset its lanes. if (_next !== null) { // If completing this work spawned new work, do that next. We'll come @@ -19657,7 +20135,7 @@ function commitRootImpl(root, renderPriorityLevel) { } } // Check if there are any effects in the whole tree. // TODO: This is left over from the effect list implementation, where we had - // to check for the existence of `firstEffect` to satsify Flow. I think the + // to check for the existence of `firstEffect` to satisfy Flow. I think the // only other reason this optimization exists is because it affects profiling. // Reconsider whether this is necessary. @@ -19696,7 +20174,7 @@ function commitRootImpl(root, renderPriorityLevel) { recordCommitTime(); } - commitMutationEffects(root, finishedWork); + commitMutationEffects(root, finishedWork, lanes); resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after // the mutation phase, so that the previous tree is still current during @@ -19741,6 +20219,9 @@ function commitRootImpl(root, renderPriorityLevel) { } if (includesSomeLane(remainingLanes, SyncLane)) { + { + markNestedUpdateScheduled(); + } // Count the number of times the root synchronously re-renders without // finishing. If there are too many, it indicates an infinite update loop. if (root === rootWithNestedUpdates) { @@ -19754,6 +20235,12 @@ function commitRootImpl(root, renderPriorityLevel) { } onCommitRoot(finishedWork.stateNode, renderPriorityLevel); + + { + if (isDevToolsPresent) { + root.memoizedUpdaters.clear(); + } + } // additional work on this root is scheduled. ensureRootIsScheduled(root, now()); @@ -19817,6 +20304,19 @@ function flushPassiveEffects() { return false; } +function enqueuePendingPassiveProfilerEffect(fiber) { + { + pendingPassiveProfilerEffects.push(fiber); + + if (!rootDoesHavePassiveEffects) { + rootDoesHavePassiveEffects = true; + scheduleCallback(NormalPriority, function() { + flushPassiveEffects(); + return null; + }); + } + } +} function flushPassiveEffectsImpl() { if (rootWithPendingPassiveEffects === null) { @@ -19843,6 +20343,16 @@ function flushPassiveEffectsImpl() { commitPassiveUnmountEffects(root.current); commitPassiveMountEffects(root, root.current); // TODO: Move to commitPassiveMountEffects + { + var profilerEffects = pendingPassiveProfilerEffects; + pendingPassiveProfilerEffects = []; + + for (var i = 0; i < profilerEffects.length; i++) { + var _fiber = profilerEffects[i]; + commitPassiveEffectDurations(root, _fiber); + } + } + { isFlushingPassiveEffects = false; } @@ -19856,6 +20366,12 @@ function flushPassiveEffectsImpl() { onPostCommitRoot(root); + { + var stateNode = root.current.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; + } + return true; } @@ -20004,7 +20520,7 @@ function retryTimedOutBoundary(boundaryFiber, retryLane) { // The boundary fiber (a Suspense component or SuspenseList component) // previously was rendered in its fallback state. One of the promises that // suspended it has resolved, which means at least part of the tree was - // likely unblocked. Try rendering again, at a new expiration time. + // likely unblocked. Try rendering again, at a new lanes. if (retryLane === NoLane) { // TODO: Assign this to `suspenseState.retryLane`? to avoid // unnecessary entanglement? @@ -20369,6 +20885,18 @@ function warnAboutRenderPhaseUpdatesInDEV(fiber) { var IsThisRendererActing = { current: false }; +function restorePendingUpdaters(root, lanes) { + { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + memoizedUpdaters.forEach(function(schedulingFiber) { + addFiberToLanesMap(root, schedulingFiber, lanes); + }); // This function intentionally does not clear memoized updaters. + // Those may still be relevant to the current commit + // and a future one (e.g. Suspense). + } + } +} function warnIfNotScopedWithMatchingAct(fiber) { { if ( @@ -20955,8 +21483,6 @@ var hasBadMapPolyfill; } } -var debugCounter = 1; - function FiberNode(tag, pendingProps, key, mode) { // Instance this.tag = tag; @@ -21012,7 +21538,6 @@ function FiberNode(tag, pendingProps, key, mode) { { // This isn't directly used but is handy for debugging internals: - this._debugID = debugCounter++; this._debugSource = null; this._debugOwner = null; this._debugNeedsRemount = false; @@ -21092,7 +21617,6 @@ function createWorkInProgress(current, pendingProps) { { // DEV-only fields - workInProgress._debugID = current._debugID; workInProgress._debugSource = current._debugSource; workInProgress._debugOwner = current._debugOwner; workInProgress._debugHookTypes = current._debugHookTypes; @@ -21300,7 +21824,8 @@ function createFiberFromTypeAndProps( case REACT_STRICT_MODE_TYPE: fiberTag = Mode; - mode |= StrictLegacyMode | StrictEffectsMode; + mode |= StrictLegacyMode; + break; case REACT_PROFILER_TYPE: @@ -21542,7 +22067,6 @@ function assignFiberPropertiesInDEV(target, source) { target.treeBaseDuration = source.treeBaseDuration; } - target._debugID = source._debugID; target._debugSource = source._debugSource; target._debugOwner = source._debugOwner; target._debugNeedsRemount = source._debugNeedsRemount; @@ -21574,6 +22098,20 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.entangledLanes = NoLanes; this.entanglements = createLaneMap(NoLanes); + { + this.effectDuration = 0; + this.passiveEffectDuration = 0; + } + + { + this.memoizedUpdaters = new Set(); + var pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []); + + for (var i = 0; i < TotalLanes; i++) { + pendingUpdatersLaneMap.push(new Set()); + } + } + { switch (tag) { case ConcurrentRoot: @@ -21834,6 +22372,14 @@ function getPublicRootInstance(container) { } } +var shouldErrorImpl = function(fiber) { + return null; +}; + +function shouldError(fiber) { + return shouldErrorImpl(fiber); +} + var shouldSuspendImpl = function(fiber) { return false; }; @@ -21848,6 +22394,7 @@ var overrideProps = null; var overridePropsDeletePath = null; var overridePropsRenamePath = null; var scheduleUpdate = null; +var setErrorHandler = null; var setSuspenseHandler = null; { @@ -22035,6 +22582,10 @@ var setSuspenseHandler = null; scheduleUpdateOnFiber(fiber, SyncLane, NoTimestamp); }; + setErrorHandler = function(newShouldErrorImpl) { + shouldErrorImpl = newShouldErrorImpl; + }; + setSuspenseHandler = function(newShouldSuspendImpl) { shouldSuspendImpl = newShouldSuspendImpl; }; @@ -22072,6 +22623,7 @@ function injectIntoDevTools(devToolsConfig) { overrideProps: overrideProps, overridePropsDeletePath: overridePropsDeletePath, overridePropsRenamePath: overridePropsRenamePath, + setErrorHandler: setErrorHandler, setSuspenseHandler: setSuspenseHandler, scheduleUpdate: scheduleUpdate, currentDispatcherRef: ReactCurrentDispatcher, diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js b/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js index 94c758fce769ab..a7aceae346dc3e 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-prod.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<> + * @generated SignedSource<<1ef9c6e9d7c67c977fec9c16bec4cf17>> */ "use strict"; @@ -2205,8 +2205,7 @@ function readContext(context) { lastContextDependency = context; currentlyRenderingFiber.dependencies = { lanes: 0, - firstContext: context, - responders: null + firstContext: context }; } else lastContextDependency = lastContextDependency.next = context; return value; @@ -2247,7 +2246,9 @@ function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; null !== updateQueue && ((updateQueue = updateQueue.shared), - null !== workInProgressRoot && 0 !== (fiber.mode & 1) + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) ? ((fiber = updateQueue.interleaved), null === fiber ? ((update.next = update), @@ -3805,7 +3806,11 @@ function dispatchAction(fiber, queue, action) { : ((update.next = lane.next), (lane.next = update)), (queue.pending = update); else { - if (null !== workInProgressRoot && 0 !== (fiber.mode & 1)) { + if ( + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) + ) { var interleaved = queue.interleaved; null === interleaved ? ((update.next = update), @@ -4042,8 +4047,76 @@ var ContextOnlyDispatcher = { return rerenderReducer(basicStateReducer)[0]; }, unstable_isNewReconciler: !1 - }, - ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + }; +function createCapturedValue(value, source) { + return { + value: value, + source: source, + stack: getStackByFiberInDevAndProd(source) + }; +} +if ( + "function" !== + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog +) + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +function logCapturedError(boundary, errorInfo) { + try { + !1 !== + ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ + componentStack: null !== errorInfo.stack ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + null !== boundary && 1 === boundary.tag ? boundary.stateNode : null + }) && console.error(errorInfo.value); + } catch (e) { + setTimeout(function() { + throw e; + }); + } +} +var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; +function createRootErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + lane.payload = { element: null }; + var error = errorInfo.value; + lane.callback = function() { + hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); + logCapturedError(fiber, errorInfo); + }; + return lane; +} +function createClassErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if ("function" === typeof getDerivedStateFromError) { + var error = errorInfo.value; + lane.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error); + }; + } + var inst = fiber.stateNode; + null !== inst && + "function" === typeof inst.componentDidCatch && + (lane.callback = function() { + "function" !== typeof getDerivedStateFromError && + (null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) + : legacyErrorBoundariesThatAlreadyFailed.add(this), + logCapturedError(fiber, errorInfo)); + var stack = errorInfo.stack; + this.componentDidCatch(errorInfo.value, { + componentStack: null !== stack ? stack : "" + }); + }); + return lane; +} +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -4902,14 +4975,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$64 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$64 = lastTailNode), + for (var lastTailNode$69 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$69 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$64 + null === lastTailNode$69 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$64.sibling = null); + : (lastTailNode$69.sibling = null); } } function bubbleProperties(completedWork) { @@ -4919,19 +4992,19 @@ function bubbleProperties(completedWork) { newChildLanes = 0, subtreeFlags = 0; if (didBailout) - for (var child$65 = completedWork.child; null !== child$65; ) - (newChildLanes |= child$65.lanes | child$65.childLanes), - (subtreeFlags |= child$65.subtreeFlags & 1835008), - (subtreeFlags |= child$65.flags & 1835008), - (child$65.return = completedWork), - (child$65 = child$65.sibling); + for (var child$70 = completedWork.child; null !== child$70; ) + (newChildLanes |= child$70.lanes | child$70.childLanes), + (subtreeFlags |= child$70.subtreeFlags & 1835008), + (subtreeFlags |= child$70.flags & 1835008), + (child$70.return = completedWork), + (child$70 = child$70.sibling); else - for (child$65 = completedWork.child; null !== child$65; ) - (newChildLanes |= child$65.lanes | child$65.childLanes), - (subtreeFlags |= child$65.subtreeFlags), - (subtreeFlags |= child$65.flags), - (child$65.return = completedWork), - (child$65 = child$65.sibling); + for (child$70 = completedWork.child; null !== child$70; ) + (newChildLanes |= child$70.lanes | child$70.childLanes), + (subtreeFlags |= child$70.subtreeFlags), + (subtreeFlags |= child$70.flags), + (child$70.return = completedWork), + (child$70 = child$70.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -5295,74 +5368,6 @@ function unwindWork(workInProgress) { return null; } } -function createCapturedValue(value, source) { - return { - value: value, - source: source, - stack: getStackByFiberInDevAndProd(source) - }; -} -if ( - "function" !== - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog -) - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -function logCapturedError(boundary, errorInfo) { - try { - !1 !== - ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ - componentStack: null !== errorInfo.stack ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - null !== boundary && 1 === boundary.tag ? boundary.stateNode : null - }) && console.error(errorInfo.value); - } catch (e) { - setTimeout(function() { - throw e; - }); - } -} -var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; -function createRootErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - lane.payload = { element: null }; - var error = errorInfo.value; - lane.callback = function() { - hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); - logCapturedError(fiber, errorInfo); - }; - return lane; -} -function createClassErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ("function" === typeof getDerivedStateFromError) { - var error = errorInfo.value; - lane.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error); - }; - } - var inst = fiber.stateNode; - null !== inst && - "function" === typeof inst.componentDidCatch && - (lane.callback = function() { - "function" !== typeof getDerivedStateFromError && - (null === legacyErrorBoundariesThatAlreadyFailed - ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) - : legacyErrorBoundariesThatAlreadyFailed.add(this), - logCapturedError(fiber, errorInfo)); - var stack = errorInfo.stack; - this.componentDidCatch(errorInfo.value, { - componentStack: null !== stack ? stack : "" - }); - }); - return lane; -} var PossiblyWeakSet = "function" === typeof WeakSet ? WeakSet : Set, nextEffect = null; function safelyDetachRef(current, nearestMountedAncestor) { @@ -6209,6 +6214,8 @@ function requestEventTime() { } function requestUpdateLane(fiber) { if (0 === (fiber.mode & 1)) return 1; + if (0 !== (executionContext & 8) && 0 !== workInProgressRootRenderLanes) + return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; if (0 !== ReactCurrentBatchConfig.transition) return ( 0 === currentEventTransitionLane && @@ -6232,7 +6239,7 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { if (null === root) return null; markRootUpdated(root, lane, eventTime); root === workInProgressRoot && - ((workInProgressRootUpdatedLanes |= lane), + (0 === (executionContext & 8) && (workInProgressRootUpdatedLanes |= lane), 4 === workInProgressRootExitStatus && markRootSuspended$1(root, workInProgressRootRenderLanes)); 1 === lane @@ -6638,15 +6645,15 @@ function handleError(root$jscomp$0, thrownValue) { } var hasInvisibleParentBoundary = 0 !== (suspenseStackCursor.current & 1), - workInProgress$77 = returnFiber; + workInProgress$32 = returnFiber; do { var JSCompiler_temp; - if ((JSCompiler_temp = 13 === workInProgress$77.tag)) { - var nextState = workInProgress$77.memoizedState; + if ((JSCompiler_temp = 13 === workInProgress$32.tag)) { + var nextState = workInProgress$32.memoizedState; if (null !== nextState) JSCompiler_temp = null !== nextState.dehydrated ? !0 : !1; else { - var props = workInProgress$77.memoizedProps; + var props = workInProgress$32.memoizedProps; JSCompiler_temp = void 0 === props.fallback ? !1 @@ -6658,17 +6665,17 @@ function handleError(root$jscomp$0, thrownValue) { } } if (JSCompiler_temp) { - var wakeables = workInProgress$77.updateQueue; + var wakeables = workInProgress$32.updateQueue; if (null === wakeables) { var updateQueue = new Set(); updateQueue.add(wakeable); - workInProgress$77.updateQueue = updateQueue; + workInProgress$32.updateQueue = updateQueue; } else wakeables.add(wakeable); if ( - 0 === (workInProgress$77.mode & 1) && - workInProgress$77 !== returnFiber + 0 === (workInProgress$32.mode & 1) && + workInProgress$32 !== returnFiber ) { - workInProgress$77.flags |= 128; + workInProgress$32.flags |= 128; sourceFiber.flags |= 32768; sourceFiber.flags &= -10053; if (1 === sourceFiber.tag) @@ -6701,12 +6708,12 @@ function handleError(root$jscomp$0, thrownValue) { ); wakeable.then(ping, ping); } - workInProgress$77.flags |= 16384; - workInProgress$77.lanes = thrownValue; + workInProgress$32.flags |= 16384; + workInProgress$32.lanes = thrownValue; break a; } - workInProgress$77 = workInProgress$77.return; - } while (null !== workInProgress$77); + workInProgress$32 = workInProgress$32.return; + } while (null !== workInProgress$32); value = Error( (getComponentNameFromFiber(sourceFiber) || "A React component") + " suspended while rendering, but no fallback UI was specified.\n\nAdd a component higher in the tree to provide a loading indicator or placeholder to display." @@ -6715,47 +6722,47 @@ function handleError(root$jscomp$0, thrownValue) { 5 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2); value = createCapturedValue(value, sourceFiber); - workInProgress$77 = returnFiber; + workInProgress$32 = returnFiber; do { - switch (workInProgress$77.tag) { + switch (workInProgress$32.tag) { case 3: root = value; - workInProgress$77.flags |= 16384; + workInProgress$32.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$77.lanes |= thrownValue; - var update$78 = createRootErrorUpdate( - workInProgress$77, + workInProgress$32.lanes |= thrownValue; + var update$33 = createRootErrorUpdate( + workInProgress$32, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$77, update$78); + enqueueCapturedUpdate(workInProgress$32, update$33); break a; case 1: root = value; - var ctor = workInProgress$77.type, - instance = workInProgress$77.stateNode; + var ctor = workInProgress$32.type, + instance = workInProgress$32.stateNode; if ( - 0 === (workInProgress$77.flags & 128) && + 0 === (workInProgress$32.flags & 128) && ("function" === typeof ctor.getDerivedStateFromError || (null !== instance && "function" === typeof instance.componentDidCatch && (null === legacyErrorBoundariesThatAlreadyFailed || !legacyErrorBoundariesThatAlreadyFailed.has(instance)))) ) { - workInProgress$77.flags |= 16384; + workInProgress$32.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$77.lanes |= thrownValue; - var update$81 = createClassErrorUpdate( - workInProgress$77, + workInProgress$32.lanes |= thrownValue; + var update$36 = createClassErrorUpdate( + workInProgress$32, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$77, update$81); + enqueueCapturedUpdate(workInProgress$32, update$36); break a; } } - workInProgress$77 = workInProgress$77.return; - } while (null !== workInProgress$77); + workInProgress$32 = workInProgress$32.return; + } while (null !== workInProgress$32); } completeUnitOfWork(erroredWork); } catch (yetAnotherThrownValue) { @@ -7758,7 +7765,7 @@ function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = 8; - mode |= 24; + mode |= 8; break; case REACT_PROFILER_TYPE: return ( @@ -7990,7 +7997,7 @@ var roots = new Map(), devToolsConfig$jscomp$inline_986 = { findFiberByHostInstance: getInstanceFromTag, bundleType: 0, - version: "17.0.3-2d8d133e1", + version: "17.0.3-0eea57724", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function() { @@ -8016,6 +8023,7 @@ var internals$jscomp$inline_1243 = { overrideProps: null, overridePropsDeletePath: null, overridePropsRenamePath: null, + setErrorHandler: null, setSuspenseHandler: null, scheduleUpdate: null, currentDispatcherRef: ReactSharedInternals.ReactCurrentDispatcher, @@ -8031,7 +8039,7 @@ var internals$jscomp$inline_1243 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "17.0.3-2d8d133e1" + reconcilerVersion: "17.0.3-0eea57724" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1244 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js b/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js index 9b0da71618e1f0..b3cc990414b4db 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-prod.js @@ -8,7 +8,7 @@ * @nolint * @providesModule ReactNativeRenderer-prod * @preventMunge - * @generated SignedSource<<8960cfed6037c61be089c6fd4cf802ce>> + * @generated SignedSource<<7b8ef8f3a7db971deff747e55ae112f9>> */ "use strict"; @@ -2204,8 +2204,7 @@ function readContext(context) { lastContextDependency = context; currentlyRenderingFiber.dependencies = { lanes: 0, - firstContext: context, - responders: null + firstContext: context }; } else lastContextDependency = lastContextDependency.next = context; return value; @@ -2246,7 +2245,9 @@ function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; null !== updateQueue && ((updateQueue = updateQueue.shared), - null !== workInProgressRoot && 0 !== (fiber.mode & 1) + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) ? ((fiber = updateQueue.interleaved), null === fiber ? ((update.next = update), @@ -3804,7 +3805,11 @@ function dispatchAction(fiber, queue, action) { : ((update.next = lane.next), (lane.next = update)), (queue.pending = update); else { - if (null !== workInProgressRoot && 0 !== (fiber.mode & 1)) { + if ( + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) + ) { var interleaved = queue.interleaved; null === interleaved ? ((update.next = update), @@ -4041,8 +4046,76 @@ var ContextOnlyDispatcher = { return rerenderReducer(basicStateReducer)[0]; }, unstable_isNewReconciler: !1 - }, - ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + }; +function createCapturedValue(value, source) { + return { + value: value, + source: source, + stack: getStackByFiberInDevAndProd(source) + }; +} +if ( + "function" !== + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog +) + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +function logCapturedError(boundary, errorInfo) { + try { + !1 !== + ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ + componentStack: null !== errorInfo.stack ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + null !== boundary && 1 === boundary.tag ? boundary.stateNode : null + }) && console.error(errorInfo.value); + } catch (e) { + setTimeout(function() { + throw e; + }); + } +} +var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; +function createRootErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + lane.payload = { element: null }; + var error = errorInfo.value; + lane.callback = function() { + hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); + logCapturedError(fiber, errorInfo); + }; + return lane; +} +function createClassErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if ("function" === typeof getDerivedStateFromError) { + var error = errorInfo.value; + lane.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error); + }; + } + var inst = fiber.stateNode; + null !== inst && + "function" === typeof inst.componentDidCatch && + (lane.callback = function() { + "function" !== typeof getDerivedStateFromError && + (null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) + : legacyErrorBoundariesThatAlreadyFailed.add(this), + logCapturedError(fiber, errorInfo)); + var stack = errorInfo.stack; + this.componentDidCatch(errorInfo.value, { + componentStack: null !== stack ? stack : "" + }); + }); + return lane; +} +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -4901,14 +4974,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$64 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$64 = lastTailNode), + for (var lastTailNode$69 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$69 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$64 + null === lastTailNode$69 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$64.sibling = null); + : (lastTailNode$69.sibling = null); } } function bubbleProperties(completedWork) { @@ -4918,19 +4991,19 @@ function bubbleProperties(completedWork) { newChildLanes = 0, subtreeFlags = 0; if (didBailout) - for (var child$65 = completedWork.child; null !== child$65; ) - (newChildLanes |= child$65.lanes | child$65.childLanes), - (subtreeFlags |= child$65.subtreeFlags & 1835008), - (subtreeFlags |= child$65.flags & 1835008), - (child$65.return = completedWork), - (child$65 = child$65.sibling); + for (var child$70 = completedWork.child; null !== child$70; ) + (newChildLanes |= child$70.lanes | child$70.childLanes), + (subtreeFlags |= child$70.subtreeFlags & 1835008), + (subtreeFlags |= child$70.flags & 1835008), + (child$70.return = completedWork), + (child$70 = child$70.sibling); else - for (child$65 = completedWork.child; null !== child$65; ) - (newChildLanes |= child$65.lanes | child$65.childLanes), - (subtreeFlags |= child$65.subtreeFlags), - (subtreeFlags |= child$65.flags), - (child$65.return = completedWork), - (child$65 = child$65.sibling); + for (child$70 = completedWork.child; null !== child$70; ) + (newChildLanes |= child$70.lanes | child$70.childLanes), + (subtreeFlags |= child$70.subtreeFlags), + (subtreeFlags |= child$70.flags), + (child$70.return = completedWork), + (child$70 = child$70.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -5294,74 +5367,6 @@ function unwindWork(workInProgress) { return null; } } -function createCapturedValue(value, source) { - return { - value: value, - source: source, - stack: getStackByFiberInDevAndProd(source) - }; -} -if ( - "function" !== - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog -) - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -function logCapturedError(boundary, errorInfo) { - try { - !1 !== - ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ - componentStack: null !== errorInfo.stack ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - null !== boundary && 1 === boundary.tag ? boundary.stateNode : null - }) && console.error(errorInfo.value); - } catch (e) { - setTimeout(function() { - throw e; - }); - } -} -var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; -function createRootErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - lane.payload = { element: null }; - var error = errorInfo.value; - lane.callback = function() { - hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); - logCapturedError(fiber, errorInfo); - }; - return lane; -} -function createClassErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ("function" === typeof getDerivedStateFromError) { - var error = errorInfo.value; - lane.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error); - }; - } - var inst = fiber.stateNode; - null !== inst && - "function" === typeof inst.componentDidCatch && - (lane.callback = function() { - "function" !== typeof getDerivedStateFromError && - (null === legacyErrorBoundariesThatAlreadyFailed - ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) - : legacyErrorBoundariesThatAlreadyFailed.add(this), - logCapturedError(fiber, errorInfo)); - var stack = errorInfo.stack; - this.componentDidCatch(errorInfo.value, { - componentStack: null !== stack ? stack : "" - }); - }); - return lane; -} var PossiblyWeakSet = "function" === typeof WeakSet ? WeakSet : Set, nextEffect = null; function safelyDetachRef(current, nearestMountedAncestor) { @@ -6208,6 +6213,8 @@ function requestEventTime() { } function requestUpdateLane(fiber) { if (0 === (fiber.mode & 1)) return 1; + if (0 !== (executionContext & 8) && 0 !== workInProgressRootRenderLanes) + return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; if (0 !== ReactCurrentBatchConfig.transition) return ( 0 === currentEventTransitionLane && @@ -6231,7 +6238,7 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { if (null === root) return null; markRootUpdated(root, lane, eventTime); root === workInProgressRoot && - ((workInProgressRootUpdatedLanes |= lane), + (0 === (executionContext & 8) && (workInProgressRootUpdatedLanes |= lane), 4 === workInProgressRootExitStatus && markRootSuspended$1(root, workInProgressRootRenderLanes)); 1 === lane @@ -6633,15 +6640,15 @@ function handleError(root$jscomp$0, thrownValue) { } var hasInvisibleParentBoundary = 0 !== (suspenseStackCursor.current & 1), - workInProgress$77 = returnFiber; + workInProgress$32 = returnFiber; do { var JSCompiler_temp; - if ((JSCompiler_temp = 13 === workInProgress$77.tag)) { - var nextState = workInProgress$77.memoizedState; + if ((JSCompiler_temp = 13 === workInProgress$32.tag)) { + var nextState = workInProgress$32.memoizedState; if (null !== nextState) JSCompiler_temp = null !== nextState.dehydrated ? !0 : !1; else { - var props = workInProgress$77.memoizedProps; + var props = workInProgress$32.memoizedProps; JSCompiler_temp = void 0 === props.fallback ? !1 @@ -6653,17 +6660,17 @@ function handleError(root$jscomp$0, thrownValue) { } } if (JSCompiler_temp) { - var wakeables = workInProgress$77.updateQueue; + var wakeables = workInProgress$32.updateQueue; if (null === wakeables) { var updateQueue = new Set(); updateQueue.add(wakeable); - workInProgress$77.updateQueue = updateQueue; + workInProgress$32.updateQueue = updateQueue; } else wakeables.add(wakeable); if ( - 0 === (workInProgress$77.mode & 1) && - workInProgress$77 !== returnFiber + 0 === (workInProgress$32.mode & 1) && + workInProgress$32 !== returnFiber ) { - workInProgress$77.flags |= 128; + workInProgress$32.flags |= 128; sourceFiber.flags |= 32768; sourceFiber.flags &= -10053; if (1 === sourceFiber.tag) @@ -6696,12 +6703,12 @@ function handleError(root$jscomp$0, thrownValue) { ); wakeable.then(ping, ping); } - workInProgress$77.flags |= 16384; - workInProgress$77.lanes = thrownValue; + workInProgress$32.flags |= 16384; + workInProgress$32.lanes = thrownValue; break a; } - workInProgress$77 = workInProgress$77.return; - } while (null !== workInProgress$77); + workInProgress$32 = workInProgress$32.return; + } while (null !== workInProgress$32); value = Error( (getComponentNameFromFiber(sourceFiber) || "A React component") + " suspended while rendering, but no fallback UI was specified.\n\nAdd a component higher in the tree to provide a loading indicator or placeholder to display." @@ -6710,47 +6717,47 @@ function handleError(root$jscomp$0, thrownValue) { 5 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2); value = createCapturedValue(value, sourceFiber); - workInProgress$77 = returnFiber; + workInProgress$32 = returnFiber; do { - switch (workInProgress$77.tag) { + switch (workInProgress$32.tag) { case 3: root = value; - workInProgress$77.flags |= 16384; + workInProgress$32.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$77.lanes |= thrownValue; - var update$78 = createRootErrorUpdate( - workInProgress$77, + workInProgress$32.lanes |= thrownValue; + var update$33 = createRootErrorUpdate( + workInProgress$32, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$77, update$78); + enqueueCapturedUpdate(workInProgress$32, update$33); break a; case 1: root = value; - var ctor = workInProgress$77.type, - instance = workInProgress$77.stateNode; + var ctor = workInProgress$32.type, + instance = workInProgress$32.stateNode; if ( - 0 === (workInProgress$77.flags & 128) && + 0 === (workInProgress$32.flags & 128) && ("function" === typeof ctor.getDerivedStateFromError || (null !== instance && "function" === typeof instance.componentDidCatch && (null === legacyErrorBoundariesThatAlreadyFailed || !legacyErrorBoundariesThatAlreadyFailed.has(instance)))) ) { - workInProgress$77.flags |= 16384; + workInProgress$32.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$77.lanes |= thrownValue; - var update$81 = createClassErrorUpdate( - workInProgress$77, + workInProgress$32.lanes |= thrownValue; + var update$36 = createClassErrorUpdate( + workInProgress$32, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$77, update$81); + enqueueCapturedUpdate(workInProgress$32, update$36); break a; } } - workInProgress$77 = workInProgress$77.return; - } while (null !== workInProgress$77); + workInProgress$32 = workInProgress$32.return; + } while (null !== workInProgress$32); } completeUnitOfWork(erroredWork); } catch (yetAnotherThrownValue) { @@ -7753,7 +7760,7 @@ function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = 8; - mode |= 24; + mode |= 8; break; case REACT_PROFILER_TYPE: return ( @@ -7985,7 +7992,7 @@ var roots = new Map(), devToolsConfig$jscomp$inline_986 = { findFiberByHostInstance: getInstanceFromTag, bundleType: 0, - version: "17.0.3-experimental-2d8d133e1", + version: "17.0.3-experimental-0eea57724", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function() { @@ -8011,6 +8018,7 @@ var internals$jscomp$inline_1243 = { overrideProps: null, overridePropsDeletePath: null, overridePropsRenamePath: null, + setErrorHandler: null, setSuspenseHandler: null, scheduleUpdate: null, currentDispatcherRef: ReactSharedInternals.ReactCurrentDispatcher, @@ -8026,7 +8034,7 @@ var internals$jscomp$inline_1243 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "17.0.3-experimental-2d8d133e1" + reconcilerVersion: "17.0.3-experimental-0eea57724" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1244 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js b/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js index 04b71971de1b90..c762b64a24f0c7 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.fb.js @@ -7,7 +7,7 @@ * @noflow * @nolint * @preventMunge - * @generated SignedSource<<3b76b5ccdbbb286322dca9906d61c805>> + * @generated SignedSource<<81badfbb3d634bab53fb152322d1eaf7>> */ "use strict"; @@ -930,7 +930,7 @@ eventPluginOrder = Array.prototype.slice.call([ "ReactNativeBridgeEventPlugin" ]); recomputePluginOrdering(); -var injectedNamesToPlugins$jscomp$inline_224 = { +var injectedNamesToPlugins$jscomp$inline_227 = { ResponderEventPlugin: ResponderEventPlugin, ReactNativeBridgeEventPlugin: { eventTypes: {}, @@ -965,34 +965,34 @@ var injectedNamesToPlugins$jscomp$inline_224 = { } } }, - isOrderingDirty$jscomp$inline_225 = !1, - pluginName$jscomp$inline_226; -for (pluginName$jscomp$inline_226 in injectedNamesToPlugins$jscomp$inline_224) + isOrderingDirty$jscomp$inline_228 = !1, + pluginName$jscomp$inline_229; +for (pluginName$jscomp$inline_229 in injectedNamesToPlugins$jscomp$inline_227) if ( - injectedNamesToPlugins$jscomp$inline_224.hasOwnProperty( - pluginName$jscomp$inline_226 + injectedNamesToPlugins$jscomp$inline_227.hasOwnProperty( + pluginName$jscomp$inline_229 ) ) { - var pluginModule$jscomp$inline_227 = - injectedNamesToPlugins$jscomp$inline_224[pluginName$jscomp$inline_226]; + var pluginModule$jscomp$inline_230 = + injectedNamesToPlugins$jscomp$inline_227[pluginName$jscomp$inline_229]; if ( - !namesToPlugins.hasOwnProperty(pluginName$jscomp$inline_226) || - namesToPlugins[pluginName$jscomp$inline_226] !== - pluginModule$jscomp$inline_227 + !namesToPlugins.hasOwnProperty(pluginName$jscomp$inline_229) || + namesToPlugins[pluginName$jscomp$inline_229] !== + pluginModule$jscomp$inline_230 ) { - if (namesToPlugins[pluginName$jscomp$inline_226]) + if (namesToPlugins[pluginName$jscomp$inline_229]) throw Error( "EventPluginRegistry: Cannot inject two different event plugins using the same name, `" + - pluginName$jscomp$inline_226 + + pluginName$jscomp$inline_229 + "`." ); namesToPlugins[ - pluginName$jscomp$inline_226 - ] = pluginModule$jscomp$inline_227; - isOrderingDirty$jscomp$inline_225 = !0; + pluginName$jscomp$inline_229 + ] = pluginModule$jscomp$inline_230; + isOrderingDirty$jscomp$inline_228 = !0; } } -isOrderingDirty$jscomp$inline_225 && recomputePluginOrdering(); +isOrderingDirty$jscomp$inline_228 && recomputePluginOrdering(); var instanceCache = new Map(), instanceProps = new Map(); function getInstanceFromTag(tag) { @@ -1912,6 +1912,36 @@ function markRootEntangled(root, entangledLanes) { rootEntangledLanes &= ~lane; } } +function addFiberToLanesMap(root, fiber, lanes) { + if (isDevToolsPresent) + for (root = root.pendingUpdatersLaneMap; 0 < lanes; ) { + var index$10 = 31 - clz32(lanes), + lane = 1 << index$10; + root[index$10].add(fiber); + lanes &= ~lane; + } +} +function movePendingFibersToMemoized(root, lanes) { + if (isDevToolsPresent) + for ( + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap, + memoizedUpdaters = root.memoizedUpdaters; + 0 < lanes; + + ) { + var index$11 = 31 - clz32(lanes); + root = 1 << index$11; + index$11 = pendingUpdatersLaneMap[index$11]; + 0 < index$11.size && + (index$11.forEach(function(fiber) { + var alternate = fiber.alternate; + (null !== alternate && memoizedUpdaters.has(alternate)) || + memoizedUpdaters.add(fiber); + }), + index$11.clear()); + lanes &= ~root; + } +} var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log = Math.log, LN2 = Math.LN2; @@ -2223,8 +2253,7 @@ function readContext(context) { lastContextDependency = context; currentlyRenderingFiber.dependencies = { lanes: 0, - firstContext: context, - responders: null + firstContext: context }; } else lastContextDependency = lastContextDependency.next = context; return value; @@ -2265,7 +2294,9 @@ function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; null !== updateQueue && ((updateQueue = updateQueue.shared), - null !== workInProgressRoot && 0 !== (fiber.mode & 1) + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) ? ((fiber = updateQueue.interleaved), null === fiber ? ((update.next = update), @@ -3823,7 +3854,11 @@ function dispatchAction(fiber, queue, action) { : ((update.next = lane.next), (lane.next = update)), (queue.pending = update); else { - if (null !== workInProgressRoot && 0 !== (fiber.mode & 1)) { + if ( + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) + ) { var interleaved = queue.interleaved; null === interleaved ? ((update.next = update), @@ -4063,7 +4098,11 @@ var ContextOnlyDispatcher = { }, now$1 = Scheduler.unstable_now, commitTime = 0, - profilerStartTime = -1; + layoutEffectStartTime = -1, + profilerStartTime = -1, + passiveEffectStartTime = -1, + currentUpdateIsNested = !1, + nestedUpdateScheduled = !1; function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { if (0 <= profilerStartTime) { var elapsedTime = now$1() - profilerStartTime; @@ -4072,10 +4111,117 @@ function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { profilerStartTime = -1; } } +function recordLayoutEffectDuration(fiber) { + if (0 <= layoutEffectStartTime) { + var elapsedTime = now$1() - layoutEffectStartTime; + layoutEffectStartTime = -1; + for (fiber = fiber.return; null !== fiber; ) { + switch (fiber.tag) { + case 3: + fiber.stateNode.effectDuration += elapsedTime; + return; + case 12: + fiber.stateNode.effectDuration += elapsedTime; + return; + } + fiber = fiber.return; + } + } +} +function recordPassiveEffectDuration(fiber) { + if (0 <= passiveEffectStartTime) { + var elapsedTime = now$1() - passiveEffectStartTime; + passiveEffectStartTime = -1; + for (fiber = fiber.return; null !== fiber; ) { + switch (fiber.tag) { + case 3: + fiber = fiber.stateNode; + null !== fiber && (fiber.passiveEffectDuration += elapsedTime); + return; + case 12: + fiber = fiber.stateNode; + null !== fiber && (fiber.passiveEffectDuration += elapsedTime); + return; + } + fiber = fiber.return; + } + } +} +function startLayoutEffectTimer() { + layoutEffectStartTime = now$1(); +} function transferActualDuration(fiber) { for (var child = fiber.child; child; ) (fiber.actualDuration += child.actualDuration), (child = child.sibling); } +function createCapturedValue(value, source) { + return { + value: value, + source: source, + stack: getStackByFiberInDevAndProd(source) + }; +} +if ( + "function" !== + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog +) + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +function logCapturedError(boundary, errorInfo) { + try { + !1 !== + ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ + componentStack: null !== errorInfo.stack ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + null !== boundary && 1 === boundary.tag ? boundary.stateNode : null + }) && console.error(errorInfo.value); + } catch (e) { + setTimeout(function() { + throw e; + }); + } +} +var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; +function createRootErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + lane.payload = { element: null }; + var error = errorInfo.value; + lane.callback = function() { + hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); + logCapturedError(fiber, errorInfo); + }; + return lane; +} +function createClassErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if ("function" === typeof getDerivedStateFromError) { + var error = errorInfo.value; + lane.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error); + }; + } + var inst = fiber.stateNode; + null !== inst && + "function" === typeof inst.componentDidCatch && + (lane.callback = function() { + "function" !== typeof getDerivedStateFromError && + (null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) + : legacyErrorBoundariesThatAlreadyFailed.add(this), + logCapturedError(fiber, errorInfo)); + var stack = errorInfo.stack; + this.componentDidCatch(errorInfo.value, { + componentStack: null !== stack ? stack : "" + }); + }); + return lane; +} var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { @@ -4950,14 +5096,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$65 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$65 = lastTailNode), + for (var lastTailNode$72 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$72 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$65 + null === lastTailNode$72 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$65.sibling = null); + : (lastTailNode$72.sibling = null); } } function bubbleProperties(completedWork) { @@ -4969,53 +5115,53 @@ function bubbleProperties(completedWork) { if (didBailout) if (0 !== (completedWork.mode & 2)) { for ( - var treeBaseDuration$67 = completedWork.selfBaseDuration, - child$68 = completedWork.child; - null !== child$68; + var treeBaseDuration$74 = completedWork.selfBaseDuration, + child$75 = completedWork.child; + null !== child$75; ) - (newChildLanes |= child$68.lanes | child$68.childLanes), - (subtreeFlags |= child$68.subtreeFlags & 1835008), - (subtreeFlags |= child$68.flags & 1835008), - (treeBaseDuration$67 += child$68.treeBaseDuration), - (child$68 = child$68.sibling); - completedWork.treeBaseDuration = treeBaseDuration$67; + (newChildLanes |= child$75.lanes | child$75.childLanes), + (subtreeFlags |= child$75.subtreeFlags & 1835008), + (subtreeFlags |= child$75.flags & 1835008), + (treeBaseDuration$74 += child$75.treeBaseDuration), + (child$75 = child$75.sibling); + completedWork.treeBaseDuration = treeBaseDuration$74; } else for ( - treeBaseDuration$67 = completedWork.child; - null !== treeBaseDuration$67; + treeBaseDuration$74 = completedWork.child; + null !== treeBaseDuration$74; ) (newChildLanes |= - treeBaseDuration$67.lanes | treeBaseDuration$67.childLanes), - (subtreeFlags |= treeBaseDuration$67.subtreeFlags & 1835008), - (subtreeFlags |= treeBaseDuration$67.flags & 1835008), - (treeBaseDuration$67.return = completedWork), - (treeBaseDuration$67 = treeBaseDuration$67.sibling); + treeBaseDuration$74.lanes | treeBaseDuration$74.childLanes), + (subtreeFlags |= treeBaseDuration$74.subtreeFlags & 1835008), + (subtreeFlags |= treeBaseDuration$74.flags & 1835008), + (treeBaseDuration$74.return = completedWork), + (treeBaseDuration$74 = treeBaseDuration$74.sibling); else if (0 !== (completedWork.mode & 2)) { - treeBaseDuration$67 = completedWork.actualDuration; - child$68 = completedWork.selfBaseDuration; + treeBaseDuration$74 = completedWork.actualDuration; + child$75 = completedWork.selfBaseDuration; for (var child = completedWork.child; null !== child; ) (newChildLanes |= child.lanes | child.childLanes), (subtreeFlags |= child.subtreeFlags), (subtreeFlags |= child.flags), - (treeBaseDuration$67 += child.actualDuration), - (child$68 += child.treeBaseDuration), + (treeBaseDuration$74 += child.actualDuration), + (child$75 += child.treeBaseDuration), (child = child.sibling); - completedWork.actualDuration = treeBaseDuration$67; - completedWork.treeBaseDuration = child$68; + completedWork.actualDuration = treeBaseDuration$74; + completedWork.treeBaseDuration = child$75; } else for ( - treeBaseDuration$67 = completedWork.child; - null !== treeBaseDuration$67; + treeBaseDuration$74 = completedWork.child; + null !== treeBaseDuration$74; ) (newChildLanes |= - treeBaseDuration$67.lanes | treeBaseDuration$67.childLanes), - (subtreeFlags |= treeBaseDuration$67.subtreeFlags), - (subtreeFlags |= treeBaseDuration$67.flags), - (treeBaseDuration$67.return = completedWork), - (treeBaseDuration$67 = treeBaseDuration$67.sibling); + treeBaseDuration$74.lanes | treeBaseDuration$74.childLanes), + (subtreeFlags |= treeBaseDuration$74.subtreeFlags), + (subtreeFlags |= treeBaseDuration$74.flags), + (treeBaseDuration$74.return = completedWork), + (treeBaseDuration$74 = treeBaseDuration$74.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -5397,87 +5543,34 @@ function unwindWork(workInProgress) { return null; } } -function createCapturedValue(value, source) { - return { - value: value, - source: source, - stack: getStackByFiberInDevAndProd(source) - }; -} -if ( - "function" !== - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog -) - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -function logCapturedError(boundary, errorInfo) { - try { - !1 !== - ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ - componentStack: null !== errorInfo.stack ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - null !== boundary && 1 === boundary.tag ? boundary.stateNode : null - }) && console.error(errorInfo.value); - } catch (e) { - setTimeout(function() { - throw e; - }); - } -} -var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; -function createRootErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - lane.payload = { element: null }; - var error = errorInfo.value; - lane.callback = function() { - hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); - logCapturedError(fiber, errorInfo); - }; - return lane; -} -function createClassErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ("function" === typeof getDerivedStateFromError) { - var error = errorInfo.value; - lane.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error); - }; - } - var inst = fiber.stateNode; - null !== inst && - "function" === typeof inst.componentDidCatch && - (lane.callback = function() { - "function" !== typeof getDerivedStateFromError && - (null === legacyErrorBoundariesThatAlreadyFailed - ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) - : legacyErrorBoundariesThatAlreadyFailed.add(this), - logCapturedError(fiber, errorInfo)); - var stack = errorInfo.stack; - this.componentDidCatch(errorInfo.value, { - componentStack: null !== stack ? stack : "" - }); - }); - return lane; -} var PossiblyWeakSet = "function" === typeof WeakSet ? WeakSet : Set, - nextEffect = null; + nextEffect = null, + inProgressLanes = null, + inProgressRoot = null; function safelyDetachRef(current, nearestMountedAncestor) { var ref = current.ref; if (null !== ref) if ("function" === typeof ref) try { - ref(null); + if (current.mode & 2) + try { + startLayoutEffectTimer(), ref(null); + } finally { + recordLayoutEffectDuration(current); + } + else ref(null); } catch (refError) { captureCommitPhaseError(current, nearestMountedAncestor, refError); } else ref.current = null; } +function safelyCallDestroy(current, nearestMountedAncestor, destroy) { + try { + destroy(); + } catch (error) { + captureCommitPhaseError(current, nearestMountedAncestor, error); + } +} var focusedInstanceHandle = null, shouldFireAfterActiveInstanceBlur = !1; function commitBeforeMutationEffects(root, firstChild) { @@ -5572,7 +5665,7 @@ function commitBeforeMutationEffects(root, firstChild) { function commitHookEffectListUnmount( flags, finishedWork, - nearestMountedAncestor$jscomp$0 + nearestMountedAncestor ) { var updateQueue = finishedWork.updateQueue; updateQueue = null !== updateQueue ? updateQueue.lastEffect : null; @@ -5582,15 +5675,8 @@ function commitHookEffectListUnmount( if ((effect.tag & flags) === flags) { var destroy = effect.destroy; effect.destroy = void 0; - if (void 0 !== destroy) { - var current = finishedWork, - nearestMountedAncestor = nearestMountedAncestor$jscomp$0; - try { - destroy(); - } catch (error) { - captureCommitPhaseError(current, nearestMountedAncestor, error); - } - } + void 0 !== destroy && + safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy); } effect = effect.next; } while (effect !== updateQueue); @@ -5603,8 +5689,8 @@ function commitHookEffectListMount(tag, finishedWork) { var effect = (finishedWork = finishedWork.next); do { if ((effect.tag & tag) === tag) { - var create$86 = effect.create; - effect.destroy = create$86(); + var create$88 = effect.create; + effect.destroy = create$88(); } effect = effect.next; } while (effect !== finishedWork); @@ -5672,7 +5758,7 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) { node = node.sibling; } } -function commitUnmount(finishedRoot, current, nearestMountedAncestor$jscomp$0) { +function commitUnmount(finishedRoot, current, nearestMountedAncestor) { if (injectedHook && "function" === typeof injectedHook.onCommitFiberUnmount) try { injectedHook.onCommitFiberUnmount(rendererID, current); @@ -5692,44 +5778,46 @@ function commitUnmount(finishedRoot, current, nearestMountedAncestor$jscomp$0) { var _effect = effect, destroy = _effect.destroy; _effect = _effect.tag; - if (void 0 !== destroy && 0 !== (_effect & 2)) { - _effect = current; - var nearestMountedAncestor = nearestMountedAncestor$jscomp$0; - try { - destroy(); - } catch (error) { - captureCommitPhaseError(_effect, nearestMountedAncestor, error); - } - } + void 0 !== destroy && + 0 !== (_effect & 2) && + (current.mode & 2 + ? (startLayoutEffectTimer(), + safelyCallDestroy(current, nearestMountedAncestor, destroy), + recordLayoutEffectDuration(current)) + : safelyCallDestroy(current, nearestMountedAncestor, destroy)); effect = effect.next; } while (effect !== finishedRoot); } break; case 1: - safelyDetachRef(current, nearestMountedAncestor$jscomp$0); + safelyDetachRef(current, nearestMountedAncestor); finishedRoot = current.stateNode; if ("function" === typeof finishedRoot.componentWillUnmount) try { - (finishedRoot.props = current.memoizedProps), + if ( + ((finishedRoot.props = current.memoizedProps), (finishedRoot.state = current.memoizedState), - finishedRoot.componentWillUnmount(); + current.mode & 2) + ) + try { + startLayoutEffectTimer(), finishedRoot.componentWillUnmount(); + } finally { + recordLayoutEffectDuration(current); + } + else finishedRoot.componentWillUnmount(); } catch (unmountError) { captureCommitPhaseError( current, - nearestMountedAncestor$jscomp$0, + nearestMountedAncestor, unmountError ); } break; case 5: - safelyDetachRef(current, nearestMountedAncestor$jscomp$0); + safelyDetachRef(current, nearestMountedAncestor); break; case 4: - unmountHostComponents( - finishedRoot, - current, - nearestMountedAncestor$jscomp$0 - ); + unmountHostComponents(finishedRoot, current, nearestMountedAncestor); } } function detachFiberAfterEffects(fiber) { @@ -6016,7 +6104,14 @@ function commitWork(current, finishedWork) { case 11: case 14: case 15: - commitHookEffectListUnmount(3, finishedWork, finishedWork.return); + if (finishedWork.mode & 2) + try { + startLayoutEffectTimer(), + commitHookEffectListUnmount(3, finishedWork, finishedWork.return); + } finally { + recordLayoutEffectDuration(finishedWork); + } + else commitHookEffectListUnmount(3, finishedWork, finishedWork.return); return; case 1: return; @@ -6091,18 +6186,29 @@ function attachSuspenseRetryListeners(finishedWork) { (retryCache = finishedWork.stateNode = new PossiblyWeakSet()); wakeables.forEach(function(wakeable) { var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable); - retryCache.has(wakeable) || - (retryCache.add(wakeable), wakeable.then(retry, retry)); + if (!retryCache.has(wakeable)) { + retryCache.add(wakeable); + if (isDevToolsPresent) + if (null !== inProgressLanes && null !== inProgressRoot) + restorePendingUpdaters(inProgressRoot, inProgressLanes); + else + throw Error( + "Expected finished root and lanes to be set. This is a bug in React." + ); + wakeable.then(retry, retry); + } }); } } -function commitMutationEffects(root, firstChild) { +function commitMutationEffects(root, firstChild, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; for (nextEffect = firstChild; null !== nextEffect; ) { firstChild = nextEffect; - var deletions = firstChild.deletions; - if (null !== deletions) - for (var i = 0; i < deletions.length; i++) { - var childToDelete = deletions[i]; + committedLanes = firstChild.deletions; + if (null !== committedLanes) + for (var i = 0; i < committedLanes.length; i++) { + var childToDelete = committedLanes[i]; try { unmountHostComponents(root, childToDelete, firstChild); var alternate = childToDelete.alternate; @@ -6112,9 +6218,9 @@ function commitMutationEffects(root, firstChild) { captureCommitPhaseError(childToDelete, firstChild, error); } } - deletions = firstChild.child; - if (0 !== (firstChild.subtreeFlags & 6454) && null !== deletions) - (deletions.return = firstChild), (nextEffect = deletions); + committedLanes = firstChild.child; + if (0 !== (firstChild.subtreeFlags & 6454) && null !== committedLanes) + (committedLanes.return = firstChild), (nextEffect = committedLanes); else for (; null !== nextEffect; ) { firstChild = nextEffect; @@ -6123,11 +6229,18 @@ function commitMutationEffects(root, firstChild) { if (flags & 256) { var current = firstChild.alternate; if (null !== current) { - var currentRef = current.ref; - null !== currentRef && - ("function" === typeof currentRef - ? currentRef(null) - : (currentRef.current = null)); + committedLanes = current; + var currentRef = committedLanes.ref; + if (null !== currentRef) + if ("function" === typeof currentRef) + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), currentRef(null); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else currentRef(null); + else currentRef.current = null; } } switch (flags & 2054) { @@ -6153,70 +6266,107 @@ function commitMutationEffects(root, firstChild) { } catch (error) { captureCommitPhaseError(firstChild, firstChild.return, error); } - deletions = firstChild.sibling; - if (null !== deletions) { - deletions.return = firstChild.return; - nextEffect = deletions; + committedLanes = firstChild.sibling; + if (null !== committedLanes) { + committedLanes.return = firstChild.return; + nextEffect = committedLanes; break; } nextEffect = firstChild.return; } } + inProgressRoot = inProgressLanes = null; } -function commitLayoutEffects(finishedWork) { - for (nextEffect = finishedWork; null !== nextEffect; ) { - var fiber = nextEffect, - firstChild = fiber.child; - if (0 !== (fiber.subtreeFlags & 324) && null !== firstChild) - (firstChild.return = fiber), (nextEffect = firstChild); +function commitLayoutEffects(finishedWork, root, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; + for (nextEffect = finishedWork; null !== nextEffect; ) + if ( + ((root = nextEffect), + (committedLanes = root.child), + 0 !== (root.subtreeFlags & 324) && null !== committedLanes) + ) + (committedLanes.return = root), (nextEffect = committedLanes); else - for (fiber = finishedWork; null !== nextEffect; ) { - firstChild = nextEffect; - if (0 !== (firstChild.flags & 324)) { - var current = firstChild.alternate; + for (root = finishedWork; null !== nextEffect; ) { + committedLanes = nextEffect; + if (0 !== (committedLanes.flags & 324)) { + var current = committedLanes.alternate; try { - if (0 !== (firstChild.flags & 68)) - switch (firstChild.tag) { + if (0 !== (committedLanes.flags & 68)) + switch (committedLanes.tag) { case 0: case 11: case 15: - commitHookEffectListMount(3, firstChild); + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + commitHookEffectListMount(3, committedLanes); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else commitHookEffectListMount(3, committedLanes); break; case 1: - var instance = firstChild.stateNode; - if (firstChild.flags & 4) - if (null === current) instance.componentDidMount(); + var instance = committedLanes.stateNode; + if (committedLanes.flags & 4) + if (null === current) + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + instance.componentDidMount(); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else instance.componentDidMount(); else { var prevProps = - firstChild.elementType === firstChild.type - ? current.memoizedProps - : resolveDefaultProps( - firstChild.type, - current.memoizedProps + committedLanes.elementType === committedLanes.type + ? current.memoizedProps + : resolveDefaultProps( + committedLanes.type, + current.memoizedProps + ), + prevState = current.memoizedState; + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate ); - instance.componentDidUpdate( - prevProps, - current.memoizedState, - instance.__reactInternalSnapshotBeforeUpdate - ); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate + ); } - var updateQueue = firstChild.updateQueue; + var updateQueue = committedLanes.updateQueue; null !== updateQueue && - commitUpdateQueue(firstChild, updateQueue, instance); + commitUpdateQueue(committedLanes, updateQueue, instance); break; case 3: - var updateQueue$87 = firstChild.updateQueue; - if (null !== updateQueue$87) { - var instance$88 = null; - if (null !== firstChild.child) - switch (firstChild.child.tag) { + var updateQueue$90 = committedLanes.updateQueue; + if (null !== updateQueue$90) { + var instance$91 = null; + if (null !== committedLanes.child) + switch (committedLanes.child.tag) { case 5: - instance$88 = firstChild.child.stateNode; + instance$91 = committedLanes.child.stateNode; break; case 1: - instance$88 = firstChild.child.stateNode; + instance$91 = committedLanes.child.stateNode; } - commitUpdateQueue(firstChild, updateQueue$87, instance$88); + commitUpdateQueue( + committedLanes, + updateQueue$90, + instance$91 + ); } break; case 5: @@ -6226,18 +6376,42 @@ function commitLayoutEffects(finishedWork) { case 4: break; case 12: - var onRender = firstChild.memoizedProps.onRender; - instance$88 = commitTime; + var _finishedWork$memoize2 = committedLanes.memoizedProps, + onCommit = _finishedWork$memoize2.onCommit, + onRender = _finishedWork$memoize2.onRender, + effectDuration = committedLanes.stateNode.effectDuration; + instance$91 = commitTime; current = null === current ? "mount" : "update"; + currentUpdateIsNested && (current = "nested-update"); "function" === typeof onRender && onRender( - firstChild.memoizedProps.id, + committedLanes.memoizedProps.id, + current, + committedLanes.actualDuration, + committedLanes.treeBaseDuration, + committedLanes.actualStartTime, + instance$91 + ); + "function" === typeof onCommit && + onCommit( + committedLanes.memoizedProps.id, current, - firstChild.actualDuration, - firstChild.treeBaseDuration, - firstChild.actualStartTime, - instance$88 + effectDuration, + instance$91 ); + enqueuePendingPassiveProfilerEffect(committedLanes); + var parentFiber = committedLanes.return; + a: for (; null !== parentFiber; ) { + switch (parentFiber.tag) { + case 3: + parentFiber.stateNode.effectDuration += effectDuration; + break a; + case 12: + parentFiber.stateNode.effectDuration += effectDuration; + break a; + } + parentFiber = parentFiber.return; + } break; case 13: break; @@ -6252,40 +6426,51 @@ function commitLayoutEffects(finishedWork) { "This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue." ); } - if (firstChild.flags & 256) { - instance$88 = void 0; - var ref = firstChild.ref; + if (committedLanes.flags & 256) { + instance$91 = void 0; + current = committedLanes; + var ref = current.ref; if (null !== ref) { - var instance$jscomp$0 = firstChild.stateNode; - switch (firstChild.tag) { + var instance$jscomp$0 = current.stateNode; + switch (current.tag) { case 5: - instance$88 = instance$jscomp$0; + instance$91 = instance$jscomp$0; break; default: - instance$88 = instance$jscomp$0; + instance$91 = instance$jscomp$0; } - "function" === typeof ref - ? ref(instance$88) - : (ref.current = instance$88); + if ("function" === typeof ref) + if (current.mode & 2) + try { + startLayoutEffectTimer(), ref(instance$91); + } finally { + recordLayoutEffectDuration(current); + } + else ref(instance$91); + else ref.current = instance$91; } } } catch (error) { - captureCommitPhaseError(firstChild, firstChild.return, error); + captureCommitPhaseError( + committedLanes, + committedLanes.return, + error + ); } } - if (firstChild === fiber) { + if (committedLanes === root) { nextEffect = null; break; } - instance$88 = firstChild.sibling; - if (null !== instance$88) { - instance$88.return = firstChild.return; - nextEffect = instance$88; + instance$91 = committedLanes.sibling; + if (null !== instance$91) { + instance$91.return = committedLanes.return; + nextEffect = instance$91; break; } - nextEffect = firstChild.return; + nextEffect = committedLanes.return; } - } + inProgressRoot = inProgressLanes = null; } var ceil = Math.ceil, ReactCurrentDispatcher$2 = ReactSharedInternals.ReactCurrentDispatcher, @@ -6310,6 +6495,7 @@ var ceil = Math.ceil, rootDoesHavePassiveEffects = !1, rootWithPendingPassiveEffects = null, pendingPassiveEffectsLanes = 0, + pendingPassiveProfilerEffects = [], nestedUpdateCount = 0, rootWithNestedUpdates = null, currentEventTime = -1, @@ -6323,6 +6509,8 @@ function requestEventTime() { } function requestUpdateLane(fiber) { if (0 === (fiber.mode & 1)) return 1; + if (0 !== (executionContext & 8) && 0 !== workInProgressRootRenderLanes) + return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; if (0 !== ReactCurrentBatchConfig.transition) return ( 0 === currentEventTransitionLane && @@ -6344,9 +6532,10 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { )); var root = markUpdateLaneFromFiberToRoot(fiber, lane); if (null === root) return null; + isDevToolsPresent && addFiberToLanesMap(root, fiber, lane); markRootUpdated(root, lane, eventTime); root === workInProgressRoot && - ((workInProgressRootUpdatedLanes |= lane), + (0 === (executionContext & 8) && (workInProgressRootUpdatedLanes |= lane), 4 === workInProgressRootExitStatus && markRootSuspended$1(root, workInProgressRootRenderLanes)); 1 === lane @@ -6445,6 +6634,7 @@ function ensureRootIsScheduled(root, currentTime) { } } function performConcurrentWorkOnRoot(root, didTimeout) { + nestedUpdateScheduled = currentUpdateIsNested = !1; currentEventTime = -1; currentEventTransitionLane = 0; if (0 !== (executionContext & 24)) @@ -6471,9 +6661,17 @@ function performConcurrentWorkOnRoot(root, didTimeout) { if ( workInProgressRoot !== root || workInProgressRootRenderLanes !== didTimeout - ) - (workInProgressRootRenderTargetTime = now() + 500), - prepareFreshStack(root, didTimeout); + ) { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + 0 < memoizedUpdaters.size && + (restorePendingUpdaters(root, workInProgressRootRenderLanes), + memoizedUpdaters.clear()); + movePendingFibersToMemoized(root, didTimeout); + } + workInProgressRootRenderTargetTime = now() + 500; + prepareFreshStack(root, didTimeout); + } do try { workLoopConcurrent(); @@ -6540,14 +6738,13 @@ function performConcurrentWorkOnRoot(root, didTimeout) { markRootSuspended$1(root, lanes); if ((lanes & 4194240) === lanes) break; didTimeout = root.eventTimes; - for (JSCompiler_inline_result = -1; 0 < lanes; ) { - var index$5 = 31 - clz32(lanes); - prevDispatcher = 1 << index$5; - index$5 = didTimeout[index$5]; - index$5 > JSCompiler_inline_result && - (JSCompiler_inline_result = index$5); - lanes &= ~prevDispatcher; - } + for (JSCompiler_inline_result = -1; 0 < lanes; ) + (memoizedUpdaters = 31 - clz32(lanes)), + (prevDispatcher = 1 << memoizedUpdaters), + (memoizedUpdaters = didTimeout[memoizedUpdaters]), + memoizedUpdaters > JSCompiler_inline_result && + (JSCompiler_inline_result = memoizedUpdaters), + (lanes &= ~prevDispatcher); lanes = JSCompiler_inline_result; lanes = now() - lanes; lanes = @@ -6598,6 +6795,8 @@ function markRootSuspended$1(root, suspendedLanes) { } } function performSyncWorkOnRoot(root) { + currentUpdateIsNested = nestedUpdateScheduled; + nestedUpdateScheduled = !1; if (0 !== (executionContext & 24)) throw Error("Should not already be working."); flushPassiveEffects(); @@ -6733,6 +6932,7 @@ function handleError(root$jscomp$0, thrownValue) { value = thrownValue; thrownValue = workInProgressRootRenderLanes; sourceFiber.flags |= 8192; + isDevToolsPresent && restorePendingUpdaters(root, thrownValue); if ( null !== value && "object" === typeof value && @@ -6754,15 +6954,15 @@ function handleError(root$jscomp$0, thrownValue) { } var hasInvisibleParentBoundary = 0 !== (suspenseStackCursor.current & 1), - workInProgress$81 = returnFiber; + workInProgress$34 = returnFiber; do { var JSCompiler_temp; - if ((JSCompiler_temp = 13 === workInProgress$81.tag)) { - var nextState = workInProgress$81.memoizedState; + if ((JSCompiler_temp = 13 === workInProgress$34.tag)) { + var nextState = workInProgress$34.memoizedState; if (null !== nextState) JSCompiler_temp = null !== nextState.dehydrated ? !0 : !1; else { - var props = workInProgress$81.memoizedProps; + var props = workInProgress$34.memoizedProps; JSCompiler_temp = void 0 === props.fallback ? !1 @@ -6774,17 +6974,17 @@ function handleError(root$jscomp$0, thrownValue) { } } if (JSCompiler_temp) { - var wakeables = workInProgress$81.updateQueue; + var wakeables = workInProgress$34.updateQueue; if (null === wakeables) { var updateQueue = new Set(); updateQueue.add(wakeable); - workInProgress$81.updateQueue = updateQueue; + workInProgress$34.updateQueue = updateQueue; } else wakeables.add(wakeable); if ( - 0 === (workInProgress$81.mode & 1) && - workInProgress$81 !== returnFiber + 0 === (workInProgress$34.mode & 1) && + workInProgress$34 !== returnFiber ) { - workInProgress$81.flags |= 128; + workInProgress$34.flags |= 128; sourceFiber.flags |= 32768; sourceFiber.flags &= -10053; if (1 === sourceFiber.tag) @@ -6815,14 +7015,15 @@ function handleError(root$jscomp$0, thrownValue) { wakeable, sourceFiber ); + isDevToolsPresent && restorePendingUpdaters(root, sourceFiber); wakeable.then(ping, ping); } - workInProgress$81.flags |= 16384; - workInProgress$81.lanes = thrownValue; + workInProgress$34.flags |= 16384; + workInProgress$34.lanes = thrownValue; break a; } - workInProgress$81 = workInProgress$81.return; - } while (null !== workInProgress$81); + workInProgress$34 = workInProgress$34.return; + } while (null !== workInProgress$34); value = Error( (getComponentNameFromFiber(sourceFiber) || "A React component") + " suspended while rendering, but no fallback UI was specified.\n\nAdd a component higher in the tree to provide a loading indicator or placeholder to display." @@ -6831,47 +7032,47 @@ function handleError(root$jscomp$0, thrownValue) { 5 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2); value = createCapturedValue(value, sourceFiber); - workInProgress$81 = returnFiber; + workInProgress$34 = returnFiber; do { - switch (workInProgress$81.tag) { + switch (workInProgress$34.tag) { case 3: root = value; - workInProgress$81.flags |= 16384; + workInProgress$34.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$81.lanes |= thrownValue; - var update$82 = createRootErrorUpdate( - workInProgress$81, + workInProgress$34.lanes |= thrownValue; + var update$35 = createRootErrorUpdate( + workInProgress$34, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$81, update$82); + enqueueCapturedUpdate(workInProgress$34, update$35); break a; case 1: root = value; - var ctor = workInProgress$81.type, - instance = workInProgress$81.stateNode; + var ctor = workInProgress$34.type, + instance = workInProgress$34.stateNode; if ( - 0 === (workInProgress$81.flags & 128) && + 0 === (workInProgress$34.flags & 128) && ("function" === typeof ctor.getDerivedStateFromError || (null !== instance && "function" === typeof instance.componentDidCatch && (null === legacyErrorBoundariesThatAlreadyFailed || !legacyErrorBoundariesThatAlreadyFailed.has(instance)))) ) { - workInProgress$81.flags |= 16384; + workInProgress$34.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$81.lanes |= thrownValue; - var update$85 = createClassErrorUpdate( - workInProgress$81, + workInProgress$34.lanes |= thrownValue; + var update$38 = createClassErrorUpdate( + workInProgress$34, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$81, update$85); + enqueueCapturedUpdate(workInProgress$34, update$38); break a; } } - workInProgress$81 = workInProgress$81.return; - } while (null !== workInProgress$81); + workInProgress$34 = workInProgress$34.return; + } while (null !== workInProgress$34); } completeUnitOfWork(erroredWork); } catch (yetAnotherThrownValue) { @@ -6893,8 +7094,16 @@ function renderRootSync(root, lanes) { var prevExecutionContext = executionContext; executionContext |= 8; var prevDispatcher = pushDispatcher(); - (workInProgressRoot === root && workInProgressRootRenderLanes === lanes) || + if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + 0 < memoizedUpdaters.size && + (restorePendingUpdaters(root, workInProgressRootRenderLanes), + memoizedUpdaters.clear()); + movePendingFibersToMemoized(root, lanes); + } prepareFreshStack(root, lanes); + } do try { workLoopSync(); @@ -7035,7 +7244,7 @@ function commitRootImpl(root, renderPriorityLevel) { ReactCurrentOwner$2.current = null; commitBeforeMutationEffects(root, finishedWork); commitTime = now$1(); - commitMutationEffects(root, finishedWork); + commitMutationEffects(root, finishedWork, lanes); root.current = finishedWork; commitLayoutEffects(finishedWork, root, lanes); requestPaint(); @@ -7050,11 +7259,13 @@ function commitRootImpl(root, renderPriorityLevel) { remainingLanes = root.pendingLanes; 0 === remainingLanes && (legacyErrorBoundariesThatAlreadyFailed = null); 0 !== (remainingLanes & 1) - ? root === rootWithNestedUpdates - ? nestedUpdateCount++ - : ((nestedUpdateCount = 0), (rootWithNestedUpdates = root)) + ? ((nestedUpdateScheduled = !0), + root === rootWithNestedUpdates + ? nestedUpdateCount++ + : ((nestedUpdateCount = 0), (rootWithNestedUpdates = root))) : (nestedUpdateCount = 0); onCommitRoot(finishedWork.stateNode, renderPriorityLevel); + isDevToolsPresent && root.memoizedUpdaters.clear(); ensureRootIsScheduled(root, now()); if (hasUncaughtError) throw ((hasUncaughtError = !1), @@ -7095,12 +7306,17 @@ function flushPassiveEffects() { for (var i = 0; i < deletions.length; i++) { var fiberToDelete = deletions[i]; for (nextEffect = fiberToDelete; null !== nextEffect; ) { - var fiber$jscomp$0 = nextEffect; - switch (fiber$jscomp$0.tag) { + var fiber$jscomp$0 = nextEffect, + current = fiber$jscomp$0; + switch (current.tag) { case 0: case 11: case 15: - commitHookEffectListUnmount(4, fiber$jscomp$0, fiber); + current.mode & 2 + ? ((passiveEffectStartTime = now$1()), + commitHookEffectListUnmount(4, current, fiber), + recordPassiveEffectDuration(current)) + : commitHookEffectListUnmount(4, current, fiber); } var child$jscomp$0 = fiber$jscomp$0.child; if (null !== child$jscomp$0) @@ -7146,11 +7362,15 @@ function flushPassiveEffects() { b: for (; null !== nextEffect; ) { fiber = nextEffect; if (0 !== (fiber.flags & 1024)) - switch (fiber.tag) { + switch (((i = fiber), i.tag)) { case 0: case 11: case 15: - commitHookEffectListUnmount(5, fiber, fiber.return); + i.mode & 2 + ? ((passiveEffectStartTime = now$1()), + commitHookEffectListUnmount(5, i, i.return), + recordPassiveEffectDuration(i)) + : commitHookEffectListUnmount(5, i, i.return); } var sibling$jscomp$0 = fiber.sibling; if (null !== sibling$jscomp$0) { @@ -7172,11 +7392,18 @@ function flushPassiveEffects() { deletions = nextEffect; if (0 !== (deletions.flags & 1024)) try { - switch (deletions.tag) { + switch (((fiberToDelete = deletions), fiberToDelete.tag)) { case 0: case 11: case 15: - commitHookEffectListMount(5, deletions); + if (fiberToDelete.mode & 2) { + passiveEffectStartTime = now$1(); + try { + commitHookEffectListMount(5, fiberToDelete); + } finally { + recordPassiveEffectDuration(fiberToDelete); + } + } else commitHookEffectListMount(5, fiberToDelete); } } catch (error) { captureCommitPhaseError(deletions, deletions.return, error); @@ -7194,6 +7421,43 @@ function flushPassiveEffects() { nextEffect = deletions.return; } } + finishedWork = pendingPassiveProfilerEffects; + pendingPassiveProfilerEffects = []; + for (firstChild = 0; firstChild < finishedWork.length; firstChild++) { + var finishedWork$jscomp$0 = finishedWork[firstChild]; + if (0 !== (finishedWork$jscomp$0.flags & 4)) + switch (finishedWork$jscomp$0.tag) { + case 12: + var passiveEffectDuration = + finishedWork$jscomp$0.stateNode.passiveEffectDuration, + _finishedWork$memoize = finishedWork$jscomp$0.memoizedProps, + id = _finishedWork$memoize.id, + onPostCommit = _finishedWork$memoize.onPostCommit; + sibling$jscomp$1 = commitTime; + var phase = + null === finishedWork$jscomp$0.alternate ? "mount" : "update"; + currentUpdateIsNested && (phase = "nested-update"); + "function" === typeof onPostCommit && + onPostCommit( + id, + phase, + passiveEffectDuration, + sibling$jscomp$1 + ); + var parentFiber = finishedWork$jscomp$0.return; + b: for (; null !== parentFiber; ) { + switch (parentFiber.tag) { + case 3: + parentFiber.stateNode.passiveEffectDuration += passiveEffectDuration; + break b; + case 12: + parentFiber.stateNode.passiveEffectDuration += passiveEffectDuration; + break b; + } + parentFiber = parentFiber.return; + } + } + } executionContext = prevExecutionContext; flushSyncCallbacks(); if ( @@ -7203,6 +7467,9 @@ function flushPassiveEffects() { try { injectedHook.onPostCommitFiberRoot(rendererID, renderPriority); } catch (err) {} + var stateNode = renderPriority.current.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; JSCompiler_inline_result = !0; } return JSCompiler_inline_result; @@ -7213,6 +7480,15 @@ function flushPassiveEffects() { } return !1; } +function enqueuePendingPassiveProfilerEffect(fiber) { + pendingPassiveProfilerEffects.push(fiber); + rootDoesHavePassiveEffects || + ((rootDoesHavePassiveEffects = !0), + scheduleCallback(NormalPriority, function() { + flushPassiveEffects(); + return null; + })); +} function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) { sourceFiber = createCapturedValue(error, sourceFiber); sourceFiber = createRootErrorUpdate(rootFiber, sourceFiber, 1); @@ -7339,6 +7615,9 @@ beginWork$1 = function(current, workInProgress, renderLanes) { case 12: 0 !== (renderLanes & workInProgress.childLanes) && (workInProgress.flags |= 4); + updateLanes = workInProgress.stateNode; + updateLanes.effectDuration = 0; + updateLanes.passiveEffectDuration = 0; break; case 13: if (null !== workInProgress.memoizedState) { @@ -7633,6 +7912,9 @@ beginWork$1 = function(current, workInProgress, renderLanes) { case 12: return ( (workInProgress.flags |= 4), + (updateLanes = workInProgress.stateNode), + (updateLanes.effectDuration = 0), + (updateLanes.passiveEffectDuration = 0), reconcileChildren( current, workInProgress, @@ -7812,6 +8094,12 @@ beginWork$1 = function(current, workInProgress, renderLanes) { "). This error is likely caused by a bug in React. Please file an issue." ); }; +function restorePendingUpdaters(root, lanes) { + isDevToolsPresent && + root.memoizedUpdaters.forEach(function(schedulingFiber) { + addFiberToLanesMap(root, schedulingFiber, lanes); + }); +} function FiberNode(tag, pendingProps, key, mode) { this.tag = tag; this.key = key; @@ -7908,7 +8196,7 @@ function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = 8; - mode |= 24; + mode |= 8; break; case REACT_PROFILER_TYPE: return ( @@ -8017,6 +8305,10 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.expirationTimes = createLaneMap(-1); this.entangledLanes = this.finishedLanes = this.mutableReadLanes = this.expiredLanes = this.pingedLanes = this.suspendedLanes = this.pendingLanes = 0; this.entanglements = createLaneMap(0); + this.passiveEffectDuration = this.effectDuration = 0; + this.memoizedUpdaters = new Set(); + containerInfo = this.pendingUpdatersLaneMap = []; + for (tag = 0; 31 > tag; tag++) containerInfo.push(new Set()); } function createPortal(children, containerInfo, implementation) { var key = @@ -8138,10 +8430,10 @@ batchedUpdatesImpl = function(fn, a) { } }; var roots = new Map(), - devToolsConfig$jscomp$inline_1010 = { + devToolsConfig$jscomp$inline_1016 = { findFiberByHostInstance: getInstanceFromTag, bundleType: 0, - version: "17.0.3-2d8d133e1", + version: "17.0.3-0eea57724", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function() { @@ -8156,17 +8448,18 @@ var roots = new Map(), }.bind(null, findNodeHandle) } }; -var internals$jscomp$inline_1271 = { - bundleType: devToolsConfig$jscomp$inline_1010.bundleType, - version: devToolsConfig$jscomp$inline_1010.version, - rendererPackageName: devToolsConfig$jscomp$inline_1010.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1010.rendererConfig, +var internals$jscomp$inline_1293 = { + bundleType: devToolsConfig$jscomp$inline_1016.bundleType, + version: devToolsConfig$jscomp$inline_1016.version, + rendererPackageName: devToolsConfig$jscomp$inline_1016.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1016.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, overrideProps: null, overridePropsDeletePath: null, overridePropsRenamePath: null, + setErrorHandler: null, setSuspenseHandler: null, scheduleUpdate: null, currentDispatcherRef: ReactSharedInternals.ReactCurrentDispatcher, @@ -8175,26 +8468,26 @@ var internals$jscomp$inline_1271 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1010.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1016.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "17.0.3-2d8d133e1" + reconcilerVersion: "17.0.3-0eea57724" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_1272 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_1294 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_1272.isDisabled && - hook$jscomp$inline_1272.supportsFiber + !hook$jscomp$inline_1294.isDisabled && + hook$jscomp$inline_1294.supportsFiber ) try { - (rendererID = hook$jscomp$inline_1272.inject( - internals$jscomp$inline_1271 + (rendererID = hook$jscomp$inline_1294.inject( + internals$jscomp$inline_1293 )), - (injectedHook = hook$jscomp$inline_1272); + (injectedHook = hook$jscomp$inline_1294); } catch (err) {} } exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = { diff --git a/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js b/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js index 876b06a345a249..0d92bb019adc76 100644 --- a/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js +++ b/Libraries/Renderer/implementations/ReactNativeRenderer-profiling.js @@ -8,7 +8,7 @@ * @nolint * @providesModule ReactNativeRenderer-profiling * @preventMunge - * @generated SignedSource<<69fba3aae321ae1afa2449c55aef29f5>> + * @generated SignedSource<> */ "use strict"; @@ -931,7 +931,7 @@ eventPluginOrder = Array.prototype.slice.call([ "ReactNativeBridgeEventPlugin" ]); recomputePluginOrdering(); -var injectedNamesToPlugins$jscomp$inline_224 = { +var injectedNamesToPlugins$jscomp$inline_227 = { ResponderEventPlugin: ResponderEventPlugin, ReactNativeBridgeEventPlugin: { eventTypes: {}, @@ -966,34 +966,34 @@ var injectedNamesToPlugins$jscomp$inline_224 = { } } }, - isOrderingDirty$jscomp$inline_225 = !1, - pluginName$jscomp$inline_226; -for (pluginName$jscomp$inline_226 in injectedNamesToPlugins$jscomp$inline_224) + isOrderingDirty$jscomp$inline_228 = !1, + pluginName$jscomp$inline_229; +for (pluginName$jscomp$inline_229 in injectedNamesToPlugins$jscomp$inline_227) if ( - injectedNamesToPlugins$jscomp$inline_224.hasOwnProperty( - pluginName$jscomp$inline_226 + injectedNamesToPlugins$jscomp$inline_227.hasOwnProperty( + pluginName$jscomp$inline_229 ) ) { - var pluginModule$jscomp$inline_227 = - injectedNamesToPlugins$jscomp$inline_224[pluginName$jscomp$inline_226]; + var pluginModule$jscomp$inline_230 = + injectedNamesToPlugins$jscomp$inline_227[pluginName$jscomp$inline_229]; if ( - !namesToPlugins.hasOwnProperty(pluginName$jscomp$inline_226) || - namesToPlugins[pluginName$jscomp$inline_226] !== - pluginModule$jscomp$inline_227 + !namesToPlugins.hasOwnProperty(pluginName$jscomp$inline_229) || + namesToPlugins[pluginName$jscomp$inline_229] !== + pluginModule$jscomp$inline_230 ) { - if (namesToPlugins[pluginName$jscomp$inline_226]) + if (namesToPlugins[pluginName$jscomp$inline_229]) throw Error( "EventPluginRegistry: Cannot inject two different event plugins using the same name, `" + - pluginName$jscomp$inline_226 + + pluginName$jscomp$inline_229 + "`." ); namesToPlugins[ - pluginName$jscomp$inline_226 - ] = pluginModule$jscomp$inline_227; - isOrderingDirty$jscomp$inline_225 = !0; + pluginName$jscomp$inline_229 + ] = pluginModule$jscomp$inline_230; + isOrderingDirty$jscomp$inline_228 = !0; } } -isOrderingDirty$jscomp$inline_225 && recomputePluginOrdering(); +isOrderingDirty$jscomp$inline_228 && recomputePluginOrdering(); var instanceCache = new Map(), instanceProps = new Map(); function getInstanceFromTag(tag) { @@ -1911,6 +1911,36 @@ function markRootEntangled(root, entangledLanes) { rootEntangledLanes &= ~lane; } } +function addFiberToLanesMap(root, fiber, lanes) { + if (isDevToolsPresent) + for (root = root.pendingUpdatersLaneMap; 0 < lanes; ) { + var index$10 = 31 - clz32(lanes), + lane = 1 << index$10; + root[index$10].add(fiber); + lanes &= ~lane; + } +} +function movePendingFibersToMemoized(root, lanes) { + if (isDevToolsPresent) + for ( + var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap, + memoizedUpdaters = root.memoizedUpdaters; + 0 < lanes; + + ) { + var index$11 = 31 - clz32(lanes); + root = 1 << index$11; + index$11 = pendingUpdatersLaneMap[index$11]; + 0 < index$11.size && + (index$11.forEach(function(fiber) { + var alternate = fiber.alternate; + (null !== alternate && memoizedUpdaters.has(alternate)) || + memoizedUpdaters.add(fiber); + }), + index$11.clear()); + lanes &= ~root; + } +} var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback, log = Math.log, LN2 = Math.LN2; @@ -2222,8 +2252,7 @@ function readContext(context) { lastContextDependency = context; currentlyRenderingFiber.dependencies = { lanes: 0, - firstContext: context, - responders: null + firstContext: context }; } else lastContextDependency = lastContextDependency.next = context; return value; @@ -2264,7 +2293,9 @@ function enqueueUpdate(fiber, update) { var updateQueue = fiber.updateQueue; null !== updateQueue && ((updateQueue = updateQueue.shared), - null !== workInProgressRoot && 0 !== (fiber.mode & 1) + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) ? ((fiber = updateQueue.interleaved), null === fiber ? ((update.next = update), @@ -3822,7 +3853,11 @@ function dispatchAction(fiber, queue, action) { : ((update.next = lane.next), (lane.next = update)), (queue.pending = update); else { - if (null !== workInProgressRoot && 0 !== (fiber.mode & 1)) { + if ( + null !== workInProgressRoot && + 0 !== (fiber.mode & 1) && + 0 === (executionContext & 8) + ) { var interleaved = queue.interleaved; null === interleaved ? ((update.next = update), @@ -4062,7 +4097,11 @@ var ContextOnlyDispatcher = { }, now$1 = Scheduler.unstable_now, commitTime = 0, - profilerStartTime = -1; + layoutEffectStartTime = -1, + profilerStartTime = -1, + passiveEffectStartTime = -1, + currentUpdateIsNested = !1, + nestedUpdateScheduled = !1; function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { if (0 <= profilerStartTime) { var elapsedTime = now$1() - profilerStartTime; @@ -4071,10 +4110,117 @@ function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { profilerStartTime = -1; } } +function recordLayoutEffectDuration(fiber) { + if (0 <= layoutEffectStartTime) { + var elapsedTime = now$1() - layoutEffectStartTime; + layoutEffectStartTime = -1; + for (fiber = fiber.return; null !== fiber; ) { + switch (fiber.tag) { + case 3: + fiber.stateNode.effectDuration += elapsedTime; + return; + case 12: + fiber.stateNode.effectDuration += elapsedTime; + return; + } + fiber = fiber.return; + } + } +} +function recordPassiveEffectDuration(fiber) { + if (0 <= passiveEffectStartTime) { + var elapsedTime = now$1() - passiveEffectStartTime; + passiveEffectStartTime = -1; + for (fiber = fiber.return; null !== fiber; ) { + switch (fiber.tag) { + case 3: + fiber = fiber.stateNode; + null !== fiber && (fiber.passiveEffectDuration += elapsedTime); + return; + case 12: + fiber = fiber.stateNode; + null !== fiber && (fiber.passiveEffectDuration += elapsedTime); + return; + } + fiber = fiber.return; + } + } +} +function startLayoutEffectTimer() { + layoutEffectStartTime = now$1(); +} function transferActualDuration(fiber) { for (var child = fiber.child; child; ) (fiber.actualDuration += child.actualDuration), (child = child.sibling); } +function createCapturedValue(value, source) { + return { + value: value, + source: source, + stack: getStackByFiberInDevAndProd(source) + }; +} +if ( + "function" !== + typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog +) + throw Error( + "Expected ReactFiberErrorDialog.showErrorDialog to be a function." + ); +function logCapturedError(boundary, errorInfo) { + try { + !1 !== + ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ + componentStack: null !== errorInfo.stack ? errorInfo.stack : "", + error: errorInfo.value, + errorBoundary: + null !== boundary && 1 === boundary.tag ? boundary.stateNode : null + }) && console.error(errorInfo.value); + } catch (e) { + setTimeout(function() { + throw e; + }); + } +} +var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; +function createRootErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + lane.payload = { element: null }; + var error = errorInfo.value; + lane.callback = function() { + hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); + logCapturedError(fiber, errorInfo); + }; + return lane; +} +function createClassErrorUpdate(fiber, errorInfo, lane) { + lane = createUpdate(-1, lane); + lane.tag = 3; + var getDerivedStateFromError = fiber.type.getDerivedStateFromError; + if ("function" === typeof getDerivedStateFromError) { + var error = errorInfo.value; + lane.payload = function() { + logCapturedError(fiber, errorInfo); + return getDerivedStateFromError(error); + }; + } + var inst = fiber.stateNode; + null !== inst && + "function" === typeof inst.componentDidCatch && + (lane.callback = function() { + "function" !== typeof getDerivedStateFromError && + (null === legacyErrorBoundariesThatAlreadyFailed + ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) + : legacyErrorBoundariesThatAlreadyFailed.add(this), + logCapturedError(fiber, errorInfo)); + var stack = errorInfo.stack; + this.componentDidCatch(errorInfo.value, { + componentStack: null !== stack ? stack : "" + }); + }); + return lane; +} var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { @@ -4949,14 +5095,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$65 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$65 = lastTailNode), + for (var lastTailNode$72 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$72 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$65 + null === lastTailNode$72 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$65.sibling = null); + : (lastTailNode$72.sibling = null); } } function bubbleProperties(completedWork) { @@ -4968,53 +5114,53 @@ function bubbleProperties(completedWork) { if (didBailout) if (0 !== (completedWork.mode & 2)) { for ( - var treeBaseDuration$67 = completedWork.selfBaseDuration, - child$68 = completedWork.child; - null !== child$68; + var treeBaseDuration$74 = completedWork.selfBaseDuration, + child$75 = completedWork.child; + null !== child$75; ) - (newChildLanes |= child$68.lanes | child$68.childLanes), - (subtreeFlags |= child$68.subtreeFlags & 1835008), - (subtreeFlags |= child$68.flags & 1835008), - (treeBaseDuration$67 += child$68.treeBaseDuration), - (child$68 = child$68.sibling); - completedWork.treeBaseDuration = treeBaseDuration$67; + (newChildLanes |= child$75.lanes | child$75.childLanes), + (subtreeFlags |= child$75.subtreeFlags & 1835008), + (subtreeFlags |= child$75.flags & 1835008), + (treeBaseDuration$74 += child$75.treeBaseDuration), + (child$75 = child$75.sibling); + completedWork.treeBaseDuration = treeBaseDuration$74; } else for ( - treeBaseDuration$67 = completedWork.child; - null !== treeBaseDuration$67; + treeBaseDuration$74 = completedWork.child; + null !== treeBaseDuration$74; ) (newChildLanes |= - treeBaseDuration$67.lanes | treeBaseDuration$67.childLanes), - (subtreeFlags |= treeBaseDuration$67.subtreeFlags & 1835008), - (subtreeFlags |= treeBaseDuration$67.flags & 1835008), - (treeBaseDuration$67.return = completedWork), - (treeBaseDuration$67 = treeBaseDuration$67.sibling); + treeBaseDuration$74.lanes | treeBaseDuration$74.childLanes), + (subtreeFlags |= treeBaseDuration$74.subtreeFlags & 1835008), + (subtreeFlags |= treeBaseDuration$74.flags & 1835008), + (treeBaseDuration$74.return = completedWork), + (treeBaseDuration$74 = treeBaseDuration$74.sibling); else if (0 !== (completedWork.mode & 2)) { - treeBaseDuration$67 = completedWork.actualDuration; - child$68 = completedWork.selfBaseDuration; + treeBaseDuration$74 = completedWork.actualDuration; + child$75 = completedWork.selfBaseDuration; for (var child = completedWork.child; null !== child; ) (newChildLanes |= child.lanes | child.childLanes), (subtreeFlags |= child.subtreeFlags), (subtreeFlags |= child.flags), - (treeBaseDuration$67 += child.actualDuration), - (child$68 += child.treeBaseDuration), + (treeBaseDuration$74 += child.actualDuration), + (child$75 += child.treeBaseDuration), (child = child.sibling); - completedWork.actualDuration = treeBaseDuration$67; - completedWork.treeBaseDuration = child$68; + completedWork.actualDuration = treeBaseDuration$74; + completedWork.treeBaseDuration = child$75; } else for ( - treeBaseDuration$67 = completedWork.child; - null !== treeBaseDuration$67; + treeBaseDuration$74 = completedWork.child; + null !== treeBaseDuration$74; ) (newChildLanes |= - treeBaseDuration$67.lanes | treeBaseDuration$67.childLanes), - (subtreeFlags |= treeBaseDuration$67.subtreeFlags), - (subtreeFlags |= treeBaseDuration$67.flags), - (treeBaseDuration$67.return = completedWork), - (treeBaseDuration$67 = treeBaseDuration$67.sibling); + treeBaseDuration$74.lanes | treeBaseDuration$74.childLanes), + (subtreeFlags |= treeBaseDuration$74.subtreeFlags), + (subtreeFlags |= treeBaseDuration$74.flags), + (treeBaseDuration$74.return = completedWork), + (treeBaseDuration$74 = treeBaseDuration$74.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -5396,87 +5542,34 @@ function unwindWork(workInProgress) { return null; } } -function createCapturedValue(value, source) { - return { - value: value, - source: source, - stack: getStackByFiberInDevAndProd(source) - }; -} -if ( - "function" !== - typeof ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog -) - throw Error( - "Expected ReactFiberErrorDialog.showErrorDialog to be a function." - ); -function logCapturedError(boundary, errorInfo) { - try { - !1 !== - ReactNativePrivateInterface.ReactFiberErrorDialog.showErrorDialog({ - componentStack: null !== errorInfo.stack ? errorInfo.stack : "", - error: errorInfo.value, - errorBoundary: - null !== boundary && 1 === boundary.tag ? boundary.stateNode : null - }) && console.error(errorInfo.value); - } catch (e) { - setTimeout(function() { - throw e; - }); - } -} -var PossiblyWeakMap = "function" === typeof WeakMap ? WeakMap : Map; -function createRootErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - lane.payload = { element: null }; - var error = errorInfo.value; - lane.callback = function() { - hasUncaughtError || ((hasUncaughtError = !0), (firstUncaughtError = error)); - logCapturedError(fiber, errorInfo); - }; - return lane; -} -function createClassErrorUpdate(fiber, errorInfo, lane) { - lane = createUpdate(-1, lane); - lane.tag = 3; - var getDerivedStateFromError = fiber.type.getDerivedStateFromError; - if ("function" === typeof getDerivedStateFromError) { - var error = errorInfo.value; - lane.payload = function() { - logCapturedError(fiber, errorInfo); - return getDerivedStateFromError(error); - }; - } - var inst = fiber.stateNode; - null !== inst && - "function" === typeof inst.componentDidCatch && - (lane.callback = function() { - "function" !== typeof getDerivedStateFromError && - (null === legacyErrorBoundariesThatAlreadyFailed - ? (legacyErrorBoundariesThatAlreadyFailed = new Set([this])) - : legacyErrorBoundariesThatAlreadyFailed.add(this), - logCapturedError(fiber, errorInfo)); - var stack = errorInfo.stack; - this.componentDidCatch(errorInfo.value, { - componentStack: null !== stack ? stack : "" - }); - }); - return lane; -} var PossiblyWeakSet = "function" === typeof WeakSet ? WeakSet : Set, - nextEffect = null; + nextEffect = null, + inProgressLanes = null, + inProgressRoot = null; function safelyDetachRef(current, nearestMountedAncestor) { var ref = current.ref; if (null !== ref) if ("function" === typeof ref) try { - ref(null); + if (current.mode & 2) + try { + startLayoutEffectTimer(), ref(null); + } finally { + recordLayoutEffectDuration(current); + } + else ref(null); } catch (refError) { captureCommitPhaseError(current, nearestMountedAncestor, refError); } else ref.current = null; } +function safelyCallDestroy(current, nearestMountedAncestor, destroy) { + try { + destroy(); + } catch (error) { + captureCommitPhaseError(current, nearestMountedAncestor, error); + } +} var focusedInstanceHandle = null, shouldFireAfterActiveInstanceBlur = !1; function commitBeforeMutationEffects(root, firstChild) { @@ -5571,7 +5664,7 @@ function commitBeforeMutationEffects(root, firstChild) { function commitHookEffectListUnmount( flags, finishedWork, - nearestMountedAncestor$jscomp$0 + nearestMountedAncestor ) { var updateQueue = finishedWork.updateQueue; updateQueue = null !== updateQueue ? updateQueue.lastEffect : null; @@ -5581,15 +5674,8 @@ function commitHookEffectListUnmount( if ((effect.tag & flags) === flags) { var destroy = effect.destroy; effect.destroy = void 0; - if (void 0 !== destroy) { - var current = finishedWork, - nearestMountedAncestor = nearestMountedAncestor$jscomp$0; - try { - destroy(); - } catch (error) { - captureCommitPhaseError(current, nearestMountedAncestor, error); - } - } + void 0 !== destroy && + safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy); } effect = effect.next; } while (effect !== updateQueue); @@ -5602,8 +5688,8 @@ function commitHookEffectListMount(tag, finishedWork) { var effect = (finishedWork = finishedWork.next); do { if ((effect.tag & tag) === tag) { - var create$86 = effect.create; - effect.destroy = create$86(); + var create$88 = effect.create; + effect.destroy = create$88(); } effect = effect.next; } while (effect !== finishedWork); @@ -5671,7 +5757,7 @@ function hideOrUnhideAllChildren(finishedWork, isHidden) { node = node.sibling; } } -function commitUnmount(finishedRoot, current, nearestMountedAncestor$jscomp$0) { +function commitUnmount(finishedRoot, current, nearestMountedAncestor) { if (injectedHook && "function" === typeof injectedHook.onCommitFiberUnmount) try { injectedHook.onCommitFiberUnmount(rendererID, current); @@ -5691,44 +5777,46 @@ function commitUnmount(finishedRoot, current, nearestMountedAncestor$jscomp$0) { var _effect = effect, destroy = _effect.destroy; _effect = _effect.tag; - if (void 0 !== destroy && 0 !== (_effect & 2)) { - _effect = current; - var nearestMountedAncestor = nearestMountedAncestor$jscomp$0; - try { - destroy(); - } catch (error) { - captureCommitPhaseError(_effect, nearestMountedAncestor, error); - } - } + void 0 !== destroy && + 0 !== (_effect & 2) && + (current.mode & 2 + ? (startLayoutEffectTimer(), + safelyCallDestroy(current, nearestMountedAncestor, destroy), + recordLayoutEffectDuration(current)) + : safelyCallDestroy(current, nearestMountedAncestor, destroy)); effect = effect.next; } while (effect !== finishedRoot); } break; case 1: - safelyDetachRef(current, nearestMountedAncestor$jscomp$0); + safelyDetachRef(current, nearestMountedAncestor); finishedRoot = current.stateNode; if ("function" === typeof finishedRoot.componentWillUnmount) try { - (finishedRoot.props = current.memoizedProps), + if ( + ((finishedRoot.props = current.memoizedProps), (finishedRoot.state = current.memoizedState), - finishedRoot.componentWillUnmount(); + current.mode & 2) + ) + try { + startLayoutEffectTimer(), finishedRoot.componentWillUnmount(); + } finally { + recordLayoutEffectDuration(current); + } + else finishedRoot.componentWillUnmount(); } catch (unmountError) { captureCommitPhaseError( current, - nearestMountedAncestor$jscomp$0, + nearestMountedAncestor, unmountError ); } break; case 5: - safelyDetachRef(current, nearestMountedAncestor$jscomp$0); + safelyDetachRef(current, nearestMountedAncestor); break; case 4: - unmountHostComponents( - finishedRoot, - current, - nearestMountedAncestor$jscomp$0 - ); + unmountHostComponents(finishedRoot, current, nearestMountedAncestor); } } function detachFiberAfterEffects(fiber) { @@ -6015,7 +6103,14 @@ function commitWork(current, finishedWork) { case 11: case 14: case 15: - commitHookEffectListUnmount(3, finishedWork, finishedWork.return); + if (finishedWork.mode & 2) + try { + startLayoutEffectTimer(), + commitHookEffectListUnmount(3, finishedWork, finishedWork.return); + } finally { + recordLayoutEffectDuration(finishedWork); + } + else commitHookEffectListUnmount(3, finishedWork, finishedWork.return); return; case 1: return; @@ -6090,18 +6185,29 @@ function attachSuspenseRetryListeners(finishedWork) { (retryCache = finishedWork.stateNode = new PossiblyWeakSet()); wakeables.forEach(function(wakeable) { var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable); - retryCache.has(wakeable) || - (retryCache.add(wakeable), wakeable.then(retry, retry)); + if (!retryCache.has(wakeable)) { + retryCache.add(wakeable); + if (isDevToolsPresent) + if (null !== inProgressLanes && null !== inProgressRoot) + restorePendingUpdaters(inProgressRoot, inProgressLanes); + else + throw Error( + "Expected finished root and lanes to be set. This is a bug in React." + ); + wakeable.then(retry, retry); + } }); } } -function commitMutationEffects(root, firstChild) { +function commitMutationEffects(root, firstChild, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; for (nextEffect = firstChild; null !== nextEffect; ) { firstChild = nextEffect; - var deletions = firstChild.deletions; - if (null !== deletions) - for (var i = 0; i < deletions.length; i++) { - var childToDelete = deletions[i]; + committedLanes = firstChild.deletions; + if (null !== committedLanes) + for (var i = 0; i < committedLanes.length; i++) { + var childToDelete = committedLanes[i]; try { unmountHostComponents(root, childToDelete, firstChild); var alternate = childToDelete.alternate; @@ -6111,9 +6217,9 @@ function commitMutationEffects(root, firstChild) { captureCommitPhaseError(childToDelete, firstChild, error); } } - deletions = firstChild.child; - if (0 !== (firstChild.subtreeFlags & 6454) && null !== deletions) - (deletions.return = firstChild), (nextEffect = deletions); + committedLanes = firstChild.child; + if (0 !== (firstChild.subtreeFlags & 6454) && null !== committedLanes) + (committedLanes.return = firstChild), (nextEffect = committedLanes); else for (; null !== nextEffect; ) { firstChild = nextEffect; @@ -6122,11 +6228,18 @@ function commitMutationEffects(root, firstChild) { if (flags & 256) { var current = firstChild.alternate; if (null !== current) { - var currentRef = current.ref; - null !== currentRef && - ("function" === typeof currentRef - ? currentRef(null) - : (currentRef.current = null)); + committedLanes = current; + var currentRef = committedLanes.ref; + if (null !== currentRef) + if ("function" === typeof currentRef) + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), currentRef(null); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else currentRef(null); + else currentRef.current = null; } } switch (flags & 2054) { @@ -6152,70 +6265,107 @@ function commitMutationEffects(root, firstChild) { } catch (error) { captureCommitPhaseError(firstChild, firstChild.return, error); } - deletions = firstChild.sibling; - if (null !== deletions) { - deletions.return = firstChild.return; - nextEffect = deletions; + committedLanes = firstChild.sibling; + if (null !== committedLanes) { + committedLanes.return = firstChild.return; + nextEffect = committedLanes; break; } nextEffect = firstChild.return; } } + inProgressRoot = inProgressLanes = null; } -function commitLayoutEffects(finishedWork) { - for (nextEffect = finishedWork; null !== nextEffect; ) { - var fiber = nextEffect, - firstChild = fiber.child; - if (0 !== (fiber.subtreeFlags & 324) && null !== firstChild) - (firstChild.return = fiber), (nextEffect = firstChild); +function commitLayoutEffects(finishedWork, root, committedLanes) { + inProgressLanes = committedLanes; + inProgressRoot = root; + for (nextEffect = finishedWork; null !== nextEffect; ) + if ( + ((root = nextEffect), + (committedLanes = root.child), + 0 !== (root.subtreeFlags & 324) && null !== committedLanes) + ) + (committedLanes.return = root), (nextEffect = committedLanes); else - for (fiber = finishedWork; null !== nextEffect; ) { - firstChild = nextEffect; - if (0 !== (firstChild.flags & 324)) { - var current = firstChild.alternate; + for (root = finishedWork; null !== nextEffect; ) { + committedLanes = nextEffect; + if (0 !== (committedLanes.flags & 324)) { + var current = committedLanes.alternate; try { - if (0 !== (firstChild.flags & 68)) - switch (firstChild.tag) { + if (0 !== (committedLanes.flags & 68)) + switch (committedLanes.tag) { case 0: case 11: case 15: - commitHookEffectListMount(3, firstChild); + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + commitHookEffectListMount(3, committedLanes); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else commitHookEffectListMount(3, committedLanes); break; case 1: - var instance = firstChild.stateNode; - if (firstChild.flags & 4) - if (null === current) instance.componentDidMount(); + var instance = committedLanes.stateNode; + if (committedLanes.flags & 4) + if (null === current) + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + instance.componentDidMount(); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else instance.componentDidMount(); else { var prevProps = - firstChild.elementType === firstChild.type - ? current.memoizedProps - : resolveDefaultProps( - firstChild.type, - current.memoizedProps + committedLanes.elementType === committedLanes.type + ? current.memoizedProps + : resolveDefaultProps( + committedLanes.type, + current.memoizedProps + ), + prevState = current.memoizedState; + if (committedLanes.mode & 2) + try { + startLayoutEffectTimer(), + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate ); - instance.componentDidUpdate( - prevProps, - current.memoizedState, - instance.__reactInternalSnapshotBeforeUpdate - ); + } finally { + recordLayoutEffectDuration(committedLanes); + } + else + instance.componentDidUpdate( + prevProps, + prevState, + instance.__reactInternalSnapshotBeforeUpdate + ); } - var updateQueue = firstChild.updateQueue; + var updateQueue = committedLanes.updateQueue; null !== updateQueue && - commitUpdateQueue(firstChild, updateQueue, instance); + commitUpdateQueue(committedLanes, updateQueue, instance); break; case 3: - var updateQueue$87 = firstChild.updateQueue; - if (null !== updateQueue$87) { - var instance$88 = null; - if (null !== firstChild.child) - switch (firstChild.child.tag) { + var updateQueue$90 = committedLanes.updateQueue; + if (null !== updateQueue$90) { + var instance$91 = null; + if (null !== committedLanes.child) + switch (committedLanes.child.tag) { case 5: - instance$88 = firstChild.child.stateNode; + instance$91 = committedLanes.child.stateNode; break; case 1: - instance$88 = firstChild.child.stateNode; + instance$91 = committedLanes.child.stateNode; } - commitUpdateQueue(firstChild, updateQueue$87, instance$88); + commitUpdateQueue( + committedLanes, + updateQueue$90, + instance$91 + ); } break; case 5: @@ -6225,18 +6375,42 @@ function commitLayoutEffects(finishedWork) { case 4: break; case 12: - var onRender = firstChild.memoizedProps.onRender; - instance$88 = commitTime; + var _finishedWork$memoize2 = committedLanes.memoizedProps, + onCommit = _finishedWork$memoize2.onCommit, + onRender = _finishedWork$memoize2.onRender, + effectDuration = committedLanes.stateNode.effectDuration; + instance$91 = commitTime; current = null === current ? "mount" : "update"; + currentUpdateIsNested && (current = "nested-update"); "function" === typeof onRender && onRender( - firstChild.memoizedProps.id, + committedLanes.memoizedProps.id, + current, + committedLanes.actualDuration, + committedLanes.treeBaseDuration, + committedLanes.actualStartTime, + instance$91 + ); + "function" === typeof onCommit && + onCommit( + committedLanes.memoizedProps.id, current, - firstChild.actualDuration, - firstChild.treeBaseDuration, - firstChild.actualStartTime, - instance$88 + effectDuration, + instance$91 ); + enqueuePendingPassiveProfilerEffect(committedLanes); + var parentFiber = committedLanes.return; + a: for (; null !== parentFiber; ) { + switch (parentFiber.tag) { + case 3: + parentFiber.stateNode.effectDuration += effectDuration; + break a; + case 12: + parentFiber.stateNode.effectDuration += effectDuration; + break a; + } + parentFiber = parentFiber.return; + } break; case 13: break; @@ -6251,40 +6425,51 @@ function commitLayoutEffects(finishedWork) { "This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue." ); } - if (firstChild.flags & 256) { - instance$88 = void 0; - var ref = firstChild.ref; + if (committedLanes.flags & 256) { + instance$91 = void 0; + current = committedLanes; + var ref = current.ref; if (null !== ref) { - var instance$jscomp$0 = firstChild.stateNode; - switch (firstChild.tag) { + var instance$jscomp$0 = current.stateNode; + switch (current.tag) { case 5: - instance$88 = instance$jscomp$0; + instance$91 = instance$jscomp$0; break; default: - instance$88 = instance$jscomp$0; + instance$91 = instance$jscomp$0; } - "function" === typeof ref - ? ref(instance$88) - : (ref.current = instance$88); + if ("function" === typeof ref) + if (current.mode & 2) + try { + startLayoutEffectTimer(), ref(instance$91); + } finally { + recordLayoutEffectDuration(current); + } + else ref(instance$91); + else ref.current = instance$91; } } } catch (error) { - captureCommitPhaseError(firstChild, firstChild.return, error); + captureCommitPhaseError( + committedLanes, + committedLanes.return, + error + ); } } - if (firstChild === fiber) { + if (committedLanes === root) { nextEffect = null; break; } - instance$88 = firstChild.sibling; - if (null !== instance$88) { - instance$88.return = firstChild.return; - nextEffect = instance$88; + instance$91 = committedLanes.sibling; + if (null !== instance$91) { + instance$91.return = committedLanes.return; + nextEffect = instance$91; break; } - nextEffect = firstChild.return; + nextEffect = committedLanes.return; } - } + inProgressRoot = inProgressLanes = null; } var ceil = Math.ceil, ReactCurrentDispatcher$2 = ReactSharedInternals.ReactCurrentDispatcher, @@ -6309,6 +6494,7 @@ var ceil = Math.ceil, rootDoesHavePassiveEffects = !1, rootWithPendingPassiveEffects = null, pendingPassiveEffectsLanes = 0, + pendingPassiveProfilerEffects = [], nestedUpdateCount = 0, rootWithNestedUpdates = null, currentEventTime = -1, @@ -6322,6 +6508,8 @@ function requestEventTime() { } function requestUpdateLane(fiber) { if (0 === (fiber.mode & 1)) return 1; + if (0 !== (executionContext & 8) && 0 !== workInProgressRootRenderLanes) + return workInProgressRootRenderLanes & -workInProgressRootRenderLanes; if (0 !== ReactCurrentBatchConfig.transition) return ( 0 === currentEventTransitionLane && @@ -6343,9 +6531,10 @@ function scheduleUpdateOnFiber(fiber, lane, eventTime) { )); var root = markUpdateLaneFromFiberToRoot(fiber, lane); if (null === root) return null; + isDevToolsPresent && addFiberToLanesMap(root, fiber, lane); markRootUpdated(root, lane, eventTime); root === workInProgressRoot && - ((workInProgressRootUpdatedLanes |= lane), + (0 === (executionContext & 8) && (workInProgressRootUpdatedLanes |= lane), 4 === workInProgressRootExitStatus && markRootSuspended$1(root, workInProgressRootRenderLanes)); 1 === lane @@ -6444,6 +6633,7 @@ function ensureRootIsScheduled(root, currentTime) { } } function performConcurrentWorkOnRoot(root, didTimeout) { + nestedUpdateScheduled = currentUpdateIsNested = !1; currentEventTime = -1; currentEventTransitionLane = 0; if (0 !== (executionContext & 24)) @@ -6466,9 +6656,17 @@ function performConcurrentWorkOnRoot(root, didTimeout) { if ( workInProgressRoot !== root || workInProgressRootRenderLanes !== didTimeout - ) - (workInProgressRootRenderTargetTime = now() + 500), - prepareFreshStack(root, didTimeout); + ) { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + 0 < memoizedUpdaters.size && + (restorePendingUpdaters(root, workInProgressRootRenderLanes), + memoizedUpdaters.clear()); + movePendingFibersToMemoized(root, didTimeout); + } + workInProgressRootRenderTargetTime = now() + 500; + prepareFreshStack(root, didTimeout); + } do try { workLoopConcurrent(); @@ -6535,14 +6733,13 @@ function performConcurrentWorkOnRoot(root, didTimeout) { markRootSuspended$1(root, lanes); if ((lanes & 4194240) === lanes) break; didTimeout = root.eventTimes; - for (JSCompiler_inline_result = -1; 0 < lanes; ) { - var index$5 = 31 - clz32(lanes); - prevDispatcher = 1 << index$5; - index$5 = didTimeout[index$5]; - index$5 > JSCompiler_inline_result && - (JSCompiler_inline_result = index$5); - lanes &= ~prevDispatcher; - } + for (JSCompiler_inline_result = -1; 0 < lanes; ) + (memoizedUpdaters = 31 - clz32(lanes)), + (prevDispatcher = 1 << memoizedUpdaters), + (memoizedUpdaters = didTimeout[memoizedUpdaters]), + memoizedUpdaters > JSCompiler_inline_result && + (JSCompiler_inline_result = memoizedUpdaters), + (lanes &= ~prevDispatcher); lanes = JSCompiler_inline_result; lanes = now() - lanes; lanes = @@ -6593,6 +6790,8 @@ function markRootSuspended$1(root, suspendedLanes) { } } function performSyncWorkOnRoot(root) { + currentUpdateIsNested = nestedUpdateScheduled; + nestedUpdateScheduled = !1; if (0 !== (executionContext & 24)) throw Error("Should not already be working."); flushPassiveEffects(); @@ -6728,6 +6927,7 @@ function handleError(root$jscomp$0, thrownValue) { value = thrownValue; thrownValue = workInProgressRootRenderLanes; sourceFiber.flags |= 8192; + isDevToolsPresent && restorePendingUpdaters(root, thrownValue); if ( null !== value && "object" === typeof value && @@ -6749,15 +6949,15 @@ function handleError(root$jscomp$0, thrownValue) { } var hasInvisibleParentBoundary = 0 !== (suspenseStackCursor.current & 1), - workInProgress$81 = returnFiber; + workInProgress$34 = returnFiber; do { var JSCompiler_temp; - if ((JSCompiler_temp = 13 === workInProgress$81.tag)) { - var nextState = workInProgress$81.memoizedState; + if ((JSCompiler_temp = 13 === workInProgress$34.tag)) { + var nextState = workInProgress$34.memoizedState; if (null !== nextState) JSCompiler_temp = null !== nextState.dehydrated ? !0 : !1; else { - var props = workInProgress$81.memoizedProps; + var props = workInProgress$34.memoizedProps; JSCompiler_temp = void 0 === props.fallback ? !1 @@ -6769,17 +6969,17 @@ function handleError(root$jscomp$0, thrownValue) { } } if (JSCompiler_temp) { - var wakeables = workInProgress$81.updateQueue; + var wakeables = workInProgress$34.updateQueue; if (null === wakeables) { var updateQueue = new Set(); updateQueue.add(wakeable); - workInProgress$81.updateQueue = updateQueue; + workInProgress$34.updateQueue = updateQueue; } else wakeables.add(wakeable); if ( - 0 === (workInProgress$81.mode & 1) && - workInProgress$81 !== returnFiber + 0 === (workInProgress$34.mode & 1) && + workInProgress$34 !== returnFiber ) { - workInProgress$81.flags |= 128; + workInProgress$34.flags |= 128; sourceFiber.flags |= 32768; sourceFiber.flags &= -10053; if (1 === sourceFiber.tag) @@ -6810,14 +7010,15 @@ function handleError(root$jscomp$0, thrownValue) { wakeable, sourceFiber ); + isDevToolsPresent && restorePendingUpdaters(root, sourceFiber); wakeable.then(ping, ping); } - workInProgress$81.flags |= 16384; - workInProgress$81.lanes = thrownValue; + workInProgress$34.flags |= 16384; + workInProgress$34.lanes = thrownValue; break a; } - workInProgress$81 = workInProgress$81.return; - } while (null !== workInProgress$81); + workInProgress$34 = workInProgress$34.return; + } while (null !== workInProgress$34); value = Error( (getComponentNameFromFiber(sourceFiber) || "A React component") + " suspended while rendering, but no fallback UI was specified.\n\nAdd a component higher in the tree to provide a loading indicator or placeholder to display." @@ -6826,47 +7027,47 @@ function handleError(root$jscomp$0, thrownValue) { 5 !== workInProgressRootExitStatus && (workInProgressRootExitStatus = 2); value = createCapturedValue(value, sourceFiber); - workInProgress$81 = returnFiber; + workInProgress$34 = returnFiber; do { - switch (workInProgress$81.tag) { + switch (workInProgress$34.tag) { case 3: root = value; - workInProgress$81.flags |= 16384; + workInProgress$34.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$81.lanes |= thrownValue; - var update$82 = createRootErrorUpdate( - workInProgress$81, + workInProgress$34.lanes |= thrownValue; + var update$35 = createRootErrorUpdate( + workInProgress$34, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$81, update$82); + enqueueCapturedUpdate(workInProgress$34, update$35); break a; case 1: root = value; - var ctor = workInProgress$81.type, - instance = workInProgress$81.stateNode; + var ctor = workInProgress$34.type, + instance = workInProgress$34.stateNode; if ( - 0 === (workInProgress$81.flags & 128) && + 0 === (workInProgress$34.flags & 128) && ("function" === typeof ctor.getDerivedStateFromError || (null !== instance && "function" === typeof instance.componentDidCatch && (null === legacyErrorBoundariesThatAlreadyFailed || !legacyErrorBoundariesThatAlreadyFailed.has(instance)))) ) { - workInProgress$81.flags |= 16384; + workInProgress$34.flags |= 16384; thrownValue &= -thrownValue; - workInProgress$81.lanes |= thrownValue; - var update$85 = createClassErrorUpdate( - workInProgress$81, + workInProgress$34.lanes |= thrownValue; + var update$38 = createClassErrorUpdate( + workInProgress$34, root, thrownValue ); - enqueueCapturedUpdate(workInProgress$81, update$85); + enqueueCapturedUpdate(workInProgress$34, update$38); break a; } } - workInProgress$81 = workInProgress$81.return; - } while (null !== workInProgress$81); + workInProgress$34 = workInProgress$34.return; + } while (null !== workInProgress$34); } completeUnitOfWork(erroredWork); } catch (yetAnotherThrownValue) { @@ -6888,8 +7089,16 @@ function renderRootSync(root, lanes) { var prevExecutionContext = executionContext; executionContext |= 8; var prevDispatcher = pushDispatcher(); - (workInProgressRoot === root && workInProgressRootRenderLanes === lanes) || + if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { + if (isDevToolsPresent) { + var memoizedUpdaters = root.memoizedUpdaters; + 0 < memoizedUpdaters.size && + (restorePendingUpdaters(root, workInProgressRootRenderLanes), + memoizedUpdaters.clear()); + movePendingFibersToMemoized(root, lanes); + } prepareFreshStack(root, lanes); + } do try { workLoopSync(); @@ -7030,7 +7239,7 @@ function commitRootImpl(root, renderPriorityLevel) { ReactCurrentOwner$2.current = null; commitBeforeMutationEffects(root, finishedWork); commitTime = now$1(); - commitMutationEffects(root, finishedWork); + commitMutationEffects(root, finishedWork, lanes); root.current = finishedWork; commitLayoutEffects(finishedWork, root, lanes); requestPaint(); @@ -7045,11 +7254,13 @@ function commitRootImpl(root, renderPriorityLevel) { remainingLanes = root.pendingLanes; 0 === remainingLanes && (legacyErrorBoundariesThatAlreadyFailed = null); 0 !== (remainingLanes & 1) - ? root === rootWithNestedUpdates - ? nestedUpdateCount++ - : ((nestedUpdateCount = 0), (rootWithNestedUpdates = root)) + ? ((nestedUpdateScheduled = !0), + root === rootWithNestedUpdates + ? nestedUpdateCount++ + : ((nestedUpdateCount = 0), (rootWithNestedUpdates = root))) : (nestedUpdateCount = 0); onCommitRoot(finishedWork.stateNode, renderPriorityLevel); + isDevToolsPresent && root.memoizedUpdaters.clear(); ensureRootIsScheduled(root, now()); if (hasUncaughtError) throw ((hasUncaughtError = !1), @@ -7090,12 +7301,17 @@ function flushPassiveEffects() { for (var i = 0; i < deletions.length; i++) { var fiberToDelete = deletions[i]; for (nextEffect = fiberToDelete; null !== nextEffect; ) { - var fiber$jscomp$0 = nextEffect; - switch (fiber$jscomp$0.tag) { + var fiber$jscomp$0 = nextEffect, + current = fiber$jscomp$0; + switch (current.tag) { case 0: case 11: case 15: - commitHookEffectListUnmount(4, fiber$jscomp$0, fiber); + current.mode & 2 + ? ((passiveEffectStartTime = now$1()), + commitHookEffectListUnmount(4, current, fiber), + recordPassiveEffectDuration(current)) + : commitHookEffectListUnmount(4, current, fiber); } var child$jscomp$0 = fiber$jscomp$0.child; if (null !== child$jscomp$0) @@ -7141,11 +7357,15 @@ function flushPassiveEffects() { b: for (; null !== nextEffect; ) { fiber = nextEffect; if (0 !== (fiber.flags & 1024)) - switch (fiber.tag) { + switch (((i = fiber), i.tag)) { case 0: case 11: case 15: - commitHookEffectListUnmount(5, fiber, fiber.return); + i.mode & 2 + ? ((passiveEffectStartTime = now$1()), + commitHookEffectListUnmount(5, i, i.return), + recordPassiveEffectDuration(i)) + : commitHookEffectListUnmount(5, i, i.return); } var sibling$jscomp$0 = fiber.sibling; if (null !== sibling$jscomp$0) { @@ -7167,11 +7387,18 @@ function flushPassiveEffects() { deletions = nextEffect; if (0 !== (deletions.flags & 1024)) try { - switch (deletions.tag) { + switch (((fiberToDelete = deletions), fiberToDelete.tag)) { case 0: case 11: case 15: - commitHookEffectListMount(5, deletions); + if (fiberToDelete.mode & 2) { + passiveEffectStartTime = now$1(); + try { + commitHookEffectListMount(5, fiberToDelete); + } finally { + recordPassiveEffectDuration(fiberToDelete); + } + } else commitHookEffectListMount(5, fiberToDelete); } } catch (error) { captureCommitPhaseError(deletions, deletions.return, error); @@ -7189,6 +7416,43 @@ function flushPassiveEffects() { nextEffect = deletions.return; } } + finishedWork = pendingPassiveProfilerEffects; + pendingPassiveProfilerEffects = []; + for (firstChild = 0; firstChild < finishedWork.length; firstChild++) { + var finishedWork$jscomp$0 = finishedWork[firstChild]; + if (0 !== (finishedWork$jscomp$0.flags & 4)) + switch (finishedWork$jscomp$0.tag) { + case 12: + var passiveEffectDuration = + finishedWork$jscomp$0.stateNode.passiveEffectDuration, + _finishedWork$memoize = finishedWork$jscomp$0.memoizedProps, + id = _finishedWork$memoize.id, + onPostCommit = _finishedWork$memoize.onPostCommit; + sibling$jscomp$1 = commitTime; + var phase = + null === finishedWork$jscomp$0.alternate ? "mount" : "update"; + currentUpdateIsNested && (phase = "nested-update"); + "function" === typeof onPostCommit && + onPostCommit( + id, + phase, + passiveEffectDuration, + sibling$jscomp$1 + ); + var parentFiber = finishedWork$jscomp$0.return; + b: for (; null !== parentFiber; ) { + switch (parentFiber.tag) { + case 3: + parentFiber.stateNode.passiveEffectDuration += passiveEffectDuration; + break b; + case 12: + parentFiber.stateNode.passiveEffectDuration += passiveEffectDuration; + break b; + } + parentFiber = parentFiber.return; + } + } + } executionContext = prevExecutionContext; flushSyncCallbacks(); if ( @@ -7198,6 +7462,9 @@ function flushPassiveEffects() { try { injectedHook.onPostCommitFiberRoot(rendererID, renderPriority); } catch (err) {} + var stateNode = renderPriority.current.stateNode; + stateNode.effectDuration = 0; + stateNode.passiveEffectDuration = 0; JSCompiler_inline_result = !0; } return JSCompiler_inline_result; @@ -7208,6 +7475,15 @@ function flushPassiveEffects() { } return !1; } +function enqueuePendingPassiveProfilerEffect(fiber) { + pendingPassiveProfilerEffects.push(fiber); + rootDoesHavePassiveEffects || + ((rootDoesHavePassiveEffects = !0), + scheduleCallback(NormalPriority, function() { + flushPassiveEffects(); + return null; + })); +} function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) { sourceFiber = createCapturedValue(error, sourceFiber); sourceFiber = createRootErrorUpdate(rootFiber, sourceFiber, 1); @@ -7334,6 +7610,9 @@ beginWork$1 = function(current, workInProgress, renderLanes) { case 12: 0 !== (renderLanes & workInProgress.childLanes) && (workInProgress.flags |= 4); + updateLanes = workInProgress.stateNode; + updateLanes.effectDuration = 0; + updateLanes.passiveEffectDuration = 0; break; case 13: if (null !== workInProgress.memoizedState) { @@ -7628,6 +7907,9 @@ beginWork$1 = function(current, workInProgress, renderLanes) { case 12: return ( (workInProgress.flags |= 4), + (updateLanes = workInProgress.stateNode), + (updateLanes.effectDuration = 0), + (updateLanes.passiveEffectDuration = 0), reconcileChildren( current, workInProgress, @@ -7807,6 +8089,12 @@ beginWork$1 = function(current, workInProgress, renderLanes) { "). This error is likely caused by a bug in React. Please file an issue." ); }; +function restorePendingUpdaters(root, lanes) { + isDevToolsPresent && + root.memoizedUpdaters.forEach(function(schedulingFiber) { + addFiberToLanesMap(root, schedulingFiber, lanes); + }); +} function FiberNode(tag, pendingProps, key, mode) { this.tag = tag; this.key = key; @@ -7903,7 +8191,7 @@ function createFiberFromTypeAndProps( break; case REACT_STRICT_MODE_TYPE: fiberTag = 8; - mode |= 24; + mode |= 8; break; case REACT_PROFILER_TYPE: return ( @@ -8012,6 +8300,10 @@ function FiberRootNode(containerInfo, tag, hydrate) { this.expirationTimes = createLaneMap(-1); this.entangledLanes = this.finishedLanes = this.mutableReadLanes = this.expiredLanes = this.pingedLanes = this.suspendedLanes = this.pendingLanes = 0; this.entanglements = createLaneMap(0); + this.passiveEffectDuration = this.effectDuration = 0; + this.memoizedUpdaters = new Set(); + containerInfo = this.pendingUpdatersLaneMap = []; + for (tag = 0; 31 > tag; tag++) containerInfo.push(new Set()); } function createPortal(children, containerInfo, implementation) { var key = @@ -8133,10 +8425,10 @@ batchedUpdatesImpl = function(fn, a) { } }; var roots = new Map(), - devToolsConfig$jscomp$inline_1010 = { + devToolsConfig$jscomp$inline_1016 = { findFiberByHostInstance: getInstanceFromTag, bundleType: 0, - version: "17.0.3-experimental-2d8d133e1", + version: "17.0.3-experimental-0eea57724", rendererPackageName: "react-native-renderer", rendererConfig: { getInspectorDataForViewTag: function() { @@ -8151,17 +8443,18 @@ var roots = new Map(), }.bind(null, findNodeHandle) } }; -var internals$jscomp$inline_1271 = { - bundleType: devToolsConfig$jscomp$inline_1010.bundleType, - version: devToolsConfig$jscomp$inline_1010.version, - rendererPackageName: devToolsConfig$jscomp$inline_1010.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1010.rendererConfig, +var internals$jscomp$inline_1293 = { + bundleType: devToolsConfig$jscomp$inline_1016.bundleType, + version: devToolsConfig$jscomp$inline_1016.version, + rendererPackageName: devToolsConfig$jscomp$inline_1016.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1016.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, overrideProps: null, overridePropsDeletePath: null, overridePropsRenamePath: null, + setErrorHandler: null, setSuspenseHandler: null, scheduleUpdate: null, currentDispatcherRef: ReactSharedInternals.ReactCurrentDispatcher, @@ -8170,26 +8463,26 @@ var internals$jscomp$inline_1271 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1010.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1016.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "17.0.3-experimental-2d8d133e1" + reconcilerVersion: "17.0.3-experimental-0eea57724" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_1272 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_1294 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_1272.isDisabled && - hook$jscomp$inline_1272.supportsFiber + !hook$jscomp$inline_1294.isDisabled && + hook$jscomp$inline_1294.supportsFiber ) try { - (rendererID = hook$jscomp$inline_1272.inject( - internals$jscomp$inline_1271 + (rendererID = hook$jscomp$inline_1294.inject( + internals$jscomp$inline_1293 )), - (injectedHook = hook$jscomp$inline_1272); + (injectedHook = hook$jscomp$inline_1294); } catch (err) {} } exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {