diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index dd4e3ba16f..dab5c4b86a 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -d12bdcda69afd219f4d91cbd60d6fae2a375d35b +768f965de2d4c6be7f688562ef02382478c82e5b diff --git a/compiled/facebook-www/React-dev.modern.js b/compiled/facebook-www/React-dev.modern.js index 87f93656e4..ca9b2560ef 100644 --- a/compiled/facebook-www/React-dev.modern.js +++ b/compiled/facebook-www/React-dev.modern.js @@ -27,7 +27,7 @@ if ( } "use strict"; -var ReactVersion = "18.3.0-www-modern-770d70a2"; +var ReactVersion = "18.3.0-www-modern-4cb7601b"; // ATTENTION // When adding new symbols to this file, diff --git a/compiled/facebook-www/ReactART-dev.classic.js b/compiled/facebook-www/ReactART-dev.classic.js index 454bf8e96b..7151c992cc 100644 --- a/compiled/facebook-www/ReactART-dev.classic.js +++ b/compiled/facebook-www/ReactART-dev.classic.js @@ -69,7 +69,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = "18.3.0-www-classic-dfeb256e"; +var ReactVersion = "18.3.0-www-classic-e4add0e3"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -543,11 +543,12 @@ var Visibility = 8192; var StoreConsistency = /* */ - 16384; // It's OK to reuse this bit because these flags are mutually exclusive for + 16384; // It's OK to reuse these bits because these flags are mutually exclusive for // different fiber types. We should really be doing this for as many flags as // possible, because we're about to run out of bits. var ScheduleRetry = StoreConsistency; +var ShouldSuspendCommit = Visibility; var LifecycleEffectMask = Passive$1 | Update | Callback | Ref | Snapshot | StoreConsistency; // Union of all commit flags (flags with the lifetime of a particular commit) @@ -587,8 +588,8 @@ var LayoutStatic = var PassiveStatic = /* */ 8388608; -var SuspenseyCommit = - /* */ +var MaySuspendCommit = + /* */ 16777216; // Flag used to identify newly inserted fibers. It isn't reset after commit unlike `Placement`. var PlacementDEV = @@ -624,7 +625,7 @@ var PassiveMask = Passive$1 | Visibility | ChildDeletion; // Union of tags that // This allows certain concepts to persist without recalculating them, // e.g. whether a subtree contains passive effects or portals. -var StaticMask = LayoutStatic | PassiveStatic | RefStatic | SuspenseyCommit; +var StaticMask = LayoutStatic | PassiveStatic | RefStatic | MaySuspendCommit; var ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner; function getNearestMountedFiber(fiber) { @@ -2934,9 +2935,6 @@ function unhideTextInstance(textInstance, text) { function getInstanceFromNode(node) { throw new Error("Not implemented."); } -function maySuspendCommit(type, props) { - return false; -} function preloadInstance(type, props) { // Return true to indicate it's already loaded return true; @@ -5590,13 +5588,6 @@ function trackUsedThenable(thenableState, thenable, index) { } } } -function suspendCommit() { - // This extra indirection only exists so it can handle passing - // noopSuspenseyCommitThenable through to throwException. - // TODO: Factor the thenable check out of throwException - suspendedThenable = noopSuspenseyCommitThenable; - throw SuspenseyCommitException; -} // This is used to track the actual thenable that suspended so it can be // passed to the rest of the Suspense implementation — which, for historical // reasons, expects to receive a thenable. @@ -17697,7 +17688,11 @@ function updateHostComponent( markUpdate(workInProgress); } } -} // TODO: This should ideally move to begin phase, but currently the instance is +} // This function must be called at the very end of the complete phase, because +// it might throw to suspend, and if the resource immediately loads, the work +// loop will resume rendering as if the work-in-progress completed. So it must +// fully complete. +// TODO: This should ideally move to begin phase, but currently the instance is // not created until the complete phase. For our existing use cases, host nodes // that suspend don't have children, so it doesn't matter. But that might not // always be true in the future. @@ -17708,28 +17703,16 @@ function preloadInstanceAndSuspendIfNeeded( props, renderLanes ) { - workInProgress.flags |= SuspenseyCommit; // Check if we're rendering at a "non-urgent" priority. This is the same - // check that `useDeferredValue` does to determine whether it needs to - // defer. This is partly for gradual adoption purposes (i.e. shouldn't start - // suspending until you opt in with startTransition or Suspense) but it - // also happens to be the desired behavior for the concrete use cases we've - // thought of so far, like CSS loading, fonts, images, etc. - // TODO: We may decide to expose a way to force a fallback even during a - // sync update. - - if (!includesOnlyNonUrgentLanes(renderLanes)); - else { - // Preload the instance - var isReady = preloadInstance(); - - if (!isReady) { - if (shouldRemainOnPreviousScreen()); - else { - // Trigger a fallback rather than block the render. - suspendCommit(); - } - } - } + { + // If this flag was set previously, we can remove it. The flag + // represents whether this particular set of props might ever need to + // suspend. The safest thing to do is for maySuspendCommit to always + // return true, but if the renderer is reasonably confident that the + // underlying resource won't be evicted, it can return false as a + // performance optimization. + workInProgress.flags &= ~MaySuspendCommit; + return; + } // Mark this fiber with a flag. This gets set on all host instances } function scheduleRetryEffect(workInProgress, retryQueue) { @@ -18160,12 +18143,10 @@ function completeWork(current, workInProgress, renderLanes) { case HostComponent: { popHostContext(workInProgress); - var _type = workInProgress.type; - - var _maySuspend = maySuspendCommit(); + var _type2 = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { - updateHostComponent(current, workInProgress, _type, newProps); + updateHostComponent(current, workInProgress, _type2, newProps); if (current.ref !== workInProgress.ref) { markRef(workInProgress); @@ -18201,7 +18182,7 @@ function completeWork(current, workInProgress, renderLanes) { } else { getRootHostContainer(); - var _instance3 = createInstance(_type, newProps); + var _instance3 = createInstance(_type2, newProps); appendAllChildren(_instance3, workInProgress); workInProgress.stateNode = _instance3; // Certain renderers require commit-time effects for initial mount. @@ -18218,17 +18199,7 @@ function completeWork(current, workInProgress, renderLanes) { // will resume rendering as if the work-in-progress completed. So it must // fully complete. - if (_maySuspend) { - preloadInstanceAndSuspendIfNeeded( - workInProgress, - _type, - newProps, - renderLanes - ); - } else { - workInProgress.flags &= ~SuspenseyCommit; - } - + preloadInstanceAndSuspendIfNeeded(workInProgress); return null; } @@ -22603,13 +22574,24 @@ function commitPassiveUnmountEffects(finishedWork) { setCurrentFiber(finishedWork); commitPassiveUnmountOnFiber(finishedWork); resetCurrentFiber(); -} +} // If we're inside a brand new tree, or a tree that was already visible, then we +// should only suspend host components that have a ShouldSuspendCommit flag. +// Components without it haven't changed since the last commit, so we can skip +// over those. +// +// When we enter a tree that is being revealed (going from hidden -> visible), +// we need to suspend _any_ component that _may_ suspend. Even if they're +// already in the "current" tree. Because their visibility has changed, the +// browser may not have prerendered them yet. So we check the MaySuspendCommit +// flag instead. + +var suspenseyCommitFlag = ShouldSuspendCommit; function accumulateSuspenseyCommit(finishedWork) { accumulateSuspenseyCommitOnFiber(finishedWork); } function recursivelyAccumulateSuspenseyCommit(parentFiber) { - if (parentFiber.subtreeFlags & SuspenseyCommit) { + if (parentFiber.subtreeFlags & suspenseyCommitFlag) { var child = parentFiber.child; while (child !== null) { @@ -22624,7 +22606,7 @@ function accumulateSuspenseyCommitOnFiber(fiber) { case HostHoistable: { recursivelyAccumulateSuspenseyCommit(fiber); - if (fiber.flags & SuspenseyCommit) { + if (fiber.flags & suspenseyCommitFlag) { if (fiber.memoizedState !== null) { suspendResource(); } @@ -22640,8 +22622,36 @@ function accumulateSuspenseyCommitOnFiber(fiber) { } case HostRoot: - case HostPortal: - // eslint-disable-next-line-no-fallthrough + case HostPortal: { + { + recursivelyAccumulateSuspenseyCommit(fiber); + } + + break; + } + + case OffscreenComponent: { + var isHidden = fiber.memoizedState !== null; + + if (isHidden); + else { + var current = fiber.alternate; + var wasHidden = current !== null && current.memoizedState !== null; + + if (wasHidden) { + // This tree is being revealed. Visit all newly visible suspensey + // instances, even if they're in the current tree. + var prevFlags = suspenseyCommitFlag; + suspenseyCommitFlag = MaySuspendCommit; + recursivelyAccumulateSuspenseyCommit(fiber); + suspenseyCommitFlag = prevFlags; + } else { + recursivelyAccumulateSuspenseyCommit(fiber); + } + } + + break; + } default: { recursivelyAccumulateSuspenseyCommit(fiber); @@ -24707,15 +24717,24 @@ function shouldRemainOnPreviousScreen() { if (handler === null); else { - if (includesOnlyRetries(workInProgressRootRenderLanes)) { + if ( + includesOnlyRetries(workInProgressRootRenderLanes) || // In this context, an OffscreenLane counts as a Retry + // TODO: It's become increasingly clear that Retries and Offscreen are + // deeply connected. They probably can be unified further. + includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) + ) { // During a retry, we can suspend rendering if the nearest Suspense boundary // is the boundary of the "shell", because we're guaranteed not to block // any new content from appearing. + // + // The reason we must check if this is a retry is because it guarantees + // that suspending the work loop won't block an actual update, because + // retries don't "update" anything; they fill in fallbacks that were left + // behind by a previous transition. return handler === getShellBoundary(); } } // For all other Lanes besides Transitions and Retries, we should not wait // for the data to load. - // TODO: We should wait during Offscreen prerendering, too. return false; } diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index 93fef4b092..0b4dafb5d2 100644 --- a/compiled/facebook-www/ReactART-dev.modern.js +++ b/compiled/facebook-www/ReactART-dev.modern.js @@ -69,7 +69,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = "18.3.0-www-modern-fc6afcc9"; +var ReactVersion = "18.3.0-www-modern-7a6ee7b0"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -543,11 +543,12 @@ var Visibility = 8192; var StoreConsistency = /* */ - 16384; // It's OK to reuse this bit because these flags are mutually exclusive for + 16384; // It's OK to reuse these bits because these flags are mutually exclusive for // different fiber types. We should really be doing this for as many flags as // possible, because we're about to run out of bits. var ScheduleRetry = StoreConsistency; +var ShouldSuspendCommit = Visibility; var LifecycleEffectMask = Passive$1 | Update | Callback | Ref | Snapshot | StoreConsistency; // Union of all commit flags (flags with the lifetime of a particular commit) @@ -587,8 +588,8 @@ var LayoutStatic = var PassiveStatic = /* */ 8388608; -var SuspenseyCommit = - /* */ +var MaySuspendCommit = + /* */ 16777216; // Flag used to identify newly inserted fibers. It isn't reset after commit unlike `Placement`. var PlacementDEV = @@ -624,7 +625,7 @@ var PassiveMask = Passive$1 | Visibility | ChildDeletion; // Union of tags that // This allows certain concepts to persist without recalculating them, // e.g. whether a subtree contains passive effects or portals. -var StaticMask = LayoutStatic | PassiveStatic | RefStatic | SuspenseyCommit; +var StaticMask = LayoutStatic | PassiveStatic | RefStatic | MaySuspendCommit; var ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner; function getNearestMountedFiber(fiber) { @@ -2931,9 +2932,6 @@ function unhideTextInstance(textInstance, text) { function getInstanceFromNode(node) { throw new Error("Not implemented."); } -function maySuspendCommit(type, props) { - return false; -} function preloadInstance(type, props) { // Return true to indicate it's already loaded return true; @@ -5346,13 +5344,6 @@ function trackUsedThenable(thenableState, thenable, index) { } } } -function suspendCommit() { - // This extra indirection only exists so it can handle passing - // noopSuspenseyCommitThenable through to throwException. - // TODO: Factor the thenable check out of throwException - suspendedThenable = noopSuspenseyCommitThenable; - throw SuspenseyCommitException; -} // This is used to track the actual thenable that suspended so it can be // passed to the rest of the Suspense implementation — which, for historical // reasons, expects to receive a thenable. @@ -17386,7 +17377,11 @@ function updateHostComponent( markUpdate(workInProgress); } } -} // TODO: This should ideally move to begin phase, but currently the instance is +} // This function must be called at the very end of the complete phase, because +// it might throw to suspend, and if the resource immediately loads, the work +// loop will resume rendering as if the work-in-progress completed. So it must +// fully complete. +// TODO: This should ideally move to begin phase, but currently the instance is // not created until the complete phase. For our existing use cases, host nodes // that suspend don't have children, so it doesn't matter. But that might not // always be true in the future. @@ -17397,28 +17392,16 @@ function preloadInstanceAndSuspendIfNeeded( props, renderLanes ) { - workInProgress.flags |= SuspenseyCommit; // Check if we're rendering at a "non-urgent" priority. This is the same - // check that `useDeferredValue` does to determine whether it needs to - // defer. This is partly for gradual adoption purposes (i.e. shouldn't start - // suspending until you opt in with startTransition or Suspense) but it - // also happens to be the desired behavior for the concrete use cases we've - // thought of so far, like CSS loading, fonts, images, etc. - // TODO: We may decide to expose a way to force a fallback even during a - // sync update. - - if (!includesOnlyNonUrgentLanes(renderLanes)); - else { - // Preload the instance - var isReady = preloadInstance(); - - if (!isReady) { - if (shouldRemainOnPreviousScreen()); - else { - // Trigger a fallback rather than block the render. - suspendCommit(); - } - } - } + { + // If this flag was set previously, we can remove it. The flag + // represents whether this particular set of props might ever need to + // suspend. The safest thing to do is for maySuspendCommit to always + // return true, but if the renderer is reasonably confident that the + // underlying resource won't be evicted, it can return false as a + // performance optimization. + workInProgress.flags &= ~MaySuspendCommit; + return; + } // Mark this fiber with a flag. This gets set on all host instances } function scheduleRetryEffect(workInProgress, retryQueue) { @@ -17842,12 +17825,10 @@ function completeWork(current, workInProgress, renderLanes) { case HostComponent: { popHostContext(workInProgress); - var _type = workInProgress.type; - - var _maySuspend = maySuspendCommit(); + var _type2 = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { - updateHostComponent(current, workInProgress, _type, newProps); + updateHostComponent(current, workInProgress, _type2, newProps); if (current.ref !== workInProgress.ref) { markRef(workInProgress); @@ -17883,7 +17864,7 @@ function completeWork(current, workInProgress, renderLanes) { } else { getRootHostContainer(); - var _instance3 = createInstance(_type, newProps); + var _instance3 = createInstance(_type2, newProps); appendAllChildren(_instance3, workInProgress); workInProgress.stateNode = _instance3; // Certain renderers require commit-time effects for initial mount. @@ -17900,17 +17881,7 @@ function completeWork(current, workInProgress, renderLanes) { // will resume rendering as if the work-in-progress completed. So it must // fully complete. - if (_maySuspend) { - preloadInstanceAndSuspendIfNeeded( - workInProgress, - _type, - newProps, - renderLanes - ); - } else { - workInProgress.flags &= ~SuspenseyCommit; - } - + preloadInstanceAndSuspendIfNeeded(workInProgress); return null; } @@ -22263,13 +22234,24 @@ function commitPassiveUnmountEffects(finishedWork) { setCurrentFiber(finishedWork); commitPassiveUnmountOnFiber(finishedWork); resetCurrentFiber(); -} +} // If we're inside a brand new tree, or a tree that was already visible, then we +// should only suspend host components that have a ShouldSuspendCommit flag. +// Components without it haven't changed since the last commit, so we can skip +// over those. +// +// When we enter a tree that is being revealed (going from hidden -> visible), +// we need to suspend _any_ component that _may_ suspend. Even if they're +// already in the "current" tree. Because their visibility has changed, the +// browser may not have prerendered them yet. So we check the MaySuspendCommit +// flag instead. + +var suspenseyCommitFlag = ShouldSuspendCommit; function accumulateSuspenseyCommit(finishedWork) { accumulateSuspenseyCommitOnFiber(finishedWork); } function recursivelyAccumulateSuspenseyCommit(parentFiber) { - if (parentFiber.subtreeFlags & SuspenseyCommit) { + if (parentFiber.subtreeFlags & suspenseyCommitFlag) { var child = parentFiber.child; while (child !== null) { @@ -22284,7 +22266,7 @@ function accumulateSuspenseyCommitOnFiber(fiber) { case HostHoistable: { recursivelyAccumulateSuspenseyCommit(fiber); - if (fiber.flags & SuspenseyCommit) { + if (fiber.flags & suspenseyCommitFlag) { if (fiber.memoizedState !== null) { suspendResource(); } @@ -22300,8 +22282,36 @@ function accumulateSuspenseyCommitOnFiber(fiber) { } case HostRoot: - case HostPortal: - // eslint-disable-next-line-no-fallthrough + case HostPortal: { + { + recursivelyAccumulateSuspenseyCommit(fiber); + } + + break; + } + + case OffscreenComponent: { + var isHidden = fiber.memoizedState !== null; + + if (isHidden); + else { + var current = fiber.alternate; + var wasHidden = current !== null && current.memoizedState !== null; + + if (wasHidden) { + // This tree is being revealed. Visit all newly visible suspensey + // instances, even if they're in the current tree. + var prevFlags = suspenseyCommitFlag; + suspenseyCommitFlag = MaySuspendCommit; + recursivelyAccumulateSuspenseyCommit(fiber); + suspenseyCommitFlag = prevFlags; + } else { + recursivelyAccumulateSuspenseyCommit(fiber); + } + } + + break; + } default: { recursivelyAccumulateSuspenseyCommit(fiber); @@ -24367,15 +24377,24 @@ function shouldRemainOnPreviousScreen() { if (handler === null); else { - if (includesOnlyRetries(workInProgressRootRenderLanes)) { + if ( + includesOnlyRetries(workInProgressRootRenderLanes) || // In this context, an OffscreenLane counts as a Retry + // TODO: It's become increasingly clear that Retries and Offscreen are + // deeply connected. They probably can be unified further. + includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) + ) { // During a retry, we can suspend rendering if the nearest Suspense boundary // is the boundary of the "shell", because we're guaranteed not to block // any new content from appearing. + // + // The reason we must check if this is a retry is because it guarantees + // that suspending the work loop won't block an actual update, because + // retries don't "update" anything; they fill in fallbacks that were left + // behind by a previous transition. return handler === getShellBoundary(); } } // For all other Lanes besides Transitions and Retries, we should not wait // for the data to load. - // TODO: We should wait during Offscreen prerendering, too. return false; } diff --git a/compiled/facebook-www/ReactART-prod.classic.js b/compiled/facebook-www/ReactART-prod.classic.js index a47cddef08..9430a028d5 100644 --- a/compiled/facebook-www/ReactART-prod.classic.js +++ b/compiled/facebook-www/ReactART-prod.classic.js @@ -7473,8 +7473,9 @@ function recursivelyTraverseAtomicPassiveEffects( parentFiber = parentFiber.sibling; } } +var suspenseyCommitFlag = 8192; function recursivelyAccumulateSuspenseyCommit(parentFiber) { - if (parentFiber.subtreeFlags & 16777216) + if (parentFiber.subtreeFlags & suspenseyCommitFlag) for (parentFiber = parentFiber.child; null !== parentFiber; ) accumulateSuspenseyCommitOnFiber(parentFiber), (parentFiber = parentFiber.sibling); @@ -7483,12 +7484,27 @@ function accumulateSuspenseyCommitOnFiber(fiber) { switch (fiber.tag) { case 26: recursivelyAccumulateSuspenseyCommit(fiber); - if (fiber.flags & 16777216 && null !== fiber.memoizedState) + if (fiber.flags & suspenseyCommitFlag && null !== fiber.memoizedState) throw Error(formatProdErrorMessage(442)); break; case 5: recursivelyAccumulateSuspenseyCommit(fiber); break; + case 3: + case 4: + recursivelyAccumulateSuspenseyCommit(fiber); + break; + case 22: + if (null === fiber.memoizedState) { + var current = fiber.alternate; + null !== current && null !== current.memoizedState + ? ((current = suspenseyCommitFlag), + (suspenseyCommitFlag = 16777216), + recursivelyAccumulateSuspenseyCommit(fiber), + (suspenseyCommitFlag = current)) + : recursivelyAccumulateSuspenseyCommit(fiber); + } + break; default: recursivelyAccumulateSuspenseyCommit(fiber); } @@ -8302,11 +8318,12 @@ function handleThrow(root, thrownValue) { ? (root = null === shellBoundary ? !0 : !1) : ((root = suspenseHandlerStackCursor.current), (root = - null !== root && - (workInProgressRootRenderLanes & 125829120) === - workInProgressRootRenderLanes - ? root === shellBoundary - : !1)), + null === root || + ((workInProgressRootRenderLanes & 125829120) !== + workInProgressRootRenderLanes && + 0 === (workInProgressRootRenderLanes & 1073741824)) + ? !1 + : root === shellBoundary)), (workInProgressSuspendedReason = root && 0 === (workInProgressRootSkippedLanes & 268435455) && @@ -10016,19 +10033,19 @@ var slice = Array.prototype.slice, }; return Text; })(React.Component), - devToolsConfig$jscomp$inline_1141 = { + devToolsConfig$jscomp$inline_1146 = { findFiberByHostInstance: function () { return null; }, bundleType: 0, - version: "18.3.0-www-classic-2dcecc61", + version: "18.3.0-www-classic-9b739764", rendererPackageName: "react-art" }; -var internals$jscomp$inline_1319 = { - bundleType: devToolsConfig$jscomp$inline_1141.bundleType, - version: devToolsConfig$jscomp$inline_1141.version, - rendererPackageName: devToolsConfig$jscomp$inline_1141.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1141.rendererConfig, +var internals$jscomp$inline_1324 = { + bundleType: devToolsConfig$jscomp$inline_1146.bundleType, + version: devToolsConfig$jscomp$inline_1146.version, + rendererPackageName: devToolsConfig$jscomp$inline_1146.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1146.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -10045,26 +10062,26 @@ var internals$jscomp$inline_1319 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1141.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1146.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-classic-2dcecc61" + reconcilerVersion: "18.3.0-www-classic-9b739764" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_1320 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_1325 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_1320.isDisabled && - hook$jscomp$inline_1320.supportsFiber + !hook$jscomp$inline_1325.isDisabled && + hook$jscomp$inline_1325.supportsFiber ) try { - (rendererID = hook$jscomp$inline_1320.inject( - internals$jscomp$inline_1319 + (rendererID = hook$jscomp$inline_1325.inject( + internals$jscomp$inline_1324 )), - (injectedHook = hook$jscomp$inline_1320); + (injectedHook = hook$jscomp$inline_1325); } catch (err) {} } var Path = Mode$1.Path; diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js index 3340a4b2d7..a977e13094 100644 --- a/compiled/facebook-www/ReactART-prod.modern.js +++ b/compiled/facebook-www/ReactART-prod.modern.js @@ -7205,8 +7205,9 @@ function recursivelyTraverseAtomicPassiveEffects( parentFiber = parentFiber.sibling; } } +var suspenseyCommitFlag = 8192; function recursivelyAccumulateSuspenseyCommit(parentFiber) { - if (parentFiber.subtreeFlags & 16777216) + if (parentFiber.subtreeFlags & suspenseyCommitFlag) for (parentFiber = parentFiber.child; null !== parentFiber; ) accumulateSuspenseyCommitOnFiber(parentFiber), (parentFiber = parentFiber.sibling); @@ -7215,12 +7216,27 @@ function accumulateSuspenseyCommitOnFiber(fiber) { switch (fiber.tag) { case 26: recursivelyAccumulateSuspenseyCommit(fiber); - if (fiber.flags & 16777216 && null !== fiber.memoizedState) + if (fiber.flags & suspenseyCommitFlag && null !== fiber.memoizedState) throw Error(formatProdErrorMessage(442)); break; case 5: recursivelyAccumulateSuspenseyCommit(fiber); break; + case 3: + case 4: + recursivelyAccumulateSuspenseyCommit(fiber); + break; + case 22: + if (null === fiber.memoizedState) { + var current = fiber.alternate; + null !== current && null !== current.memoizedState + ? ((current = suspenseyCommitFlag), + (suspenseyCommitFlag = 16777216), + recursivelyAccumulateSuspenseyCommit(fiber), + (suspenseyCommitFlag = current)) + : recursivelyAccumulateSuspenseyCommit(fiber); + } + break; default: recursivelyAccumulateSuspenseyCommit(fiber); } @@ -8034,11 +8050,12 @@ function handleThrow(root, thrownValue) { ? (root = null === shellBoundary ? !0 : !1) : ((root = suspenseHandlerStackCursor.current), (root = - null !== root && - (workInProgressRootRenderLanes & 125829120) === - workInProgressRootRenderLanes - ? root === shellBoundary - : !1)), + null === root || + ((workInProgressRootRenderLanes & 125829120) !== + workInProgressRootRenderLanes && + 0 === (workInProgressRootRenderLanes & 1073741824)) + ? !1 + : root === shellBoundary)), (workInProgressSuspendedReason = root && 0 === (workInProgressRootSkippedLanes & 268435455) && @@ -9681,19 +9698,19 @@ var slice = Array.prototype.slice, }; return Text; })(React.Component), - devToolsConfig$jscomp$inline_1121 = { + devToolsConfig$jscomp$inline_1126 = { findFiberByHostInstance: function () { return null; }, bundleType: 0, - version: "18.3.0-www-modern-ef21bdef", + version: "18.3.0-www-modern-68e129ab", rendererPackageName: "react-art" }; -var internals$jscomp$inline_1299 = { - bundleType: devToolsConfig$jscomp$inline_1121.bundleType, - version: devToolsConfig$jscomp$inline_1121.version, - rendererPackageName: devToolsConfig$jscomp$inline_1121.rendererPackageName, - rendererConfig: devToolsConfig$jscomp$inline_1121.rendererConfig, +var internals$jscomp$inline_1304 = { + bundleType: devToolsConfig$jscomp$inline_1126.bundleType, + version: devToolsConfig$jscomp$inline_1126.version, + rendererPackageName: devToolsConfig$jscomp$inline_1126.rendererPackageName, + rendererConfig: devToolsConfig$jscomp$inline_1126.rendererConfig, overrideHookState: null, overrideHookStateDeletePath: null, overrideHookStateRenamePath: null, @@ -9710,26 +9727,26 @@ var internals$jscomp$inline_1299 = { return null === fiber ? null : fiber.stateNode; }, findFiberByHostInstance: - devToolsConfig$jscomp$inline_1121.findFiberByHostInstance || + devToolsConfig$jscomp$inline_1126.findFiberByHostInstance || emptyFindFiberByHostInstance, findHostInstancesForRefresh: null, scheduleRefresh: null, scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-modern-ef21bdef" + reconcilerVersion: "18.3.0-www-modern-68e129ab" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { - var hook$jscomp$inline_1300 = __REACT_DEVTOOLS_GLOBAL_HOOK__; + var hook$jscomp$inline_1305 = __REACT_DEVTOOLS_GLOBAL_HOOK__; if ( - !hook$jscomp$inline_1300.isDisabled && - hook$jscomp$inline_1300.supportsFiber + !hook$jscomp$inline_1305.isDisabled && + hook$jscomp$inline_1305.supportsFiber ) try { - (rendererID = hook$jscomp$inline_1300.inject( - internals$jscomp$inline_1299 + (rendererID = hook$jscomp$inline_1305.inject( + internals$jscomp$inline_1304 )), - (injectedHook = hook$jscomp$inline_1300); + (injectedHook = hook$jscomp$inline_1305); } catch (err) {} } var Path = Mode$1.Path; diff --git a/compiled/facebook-www/ReactDOM-dev.classic.js b/compiled/facebook-www/ReactDOM-dev.classic.js index e9ca774f5f..3246850bf9 100644 --- a/compiled/facebook-www/ReactDOM-dev.classic.js +++ b/compiled/facebook-www/ReactDOM-dev.classic.js @@ -513,11 +513,12 @@ var Visibility = 8192; var StoreConsistency = /* */ - 16384; // It's OK to reuse this bit because these flags are mutually exclusive for + 16384; // It's OK to reuse these bits because these flags are mutually exclusive for // different fiber types. We should really be doing this for as many flags as // possible, because we're about to run out of bits. var ScheduleRetry = StoreConsistency; +var ShouldSuspendCommit = Visibility; var LifecycleEffectMask = Passive$1 | Update | Callback | Ref | Snapshot | StoreConsistency; // Union of all commit flags (flags with the lifetime of a particular commit) @@ -557,8 +558,8 @@ var LayoutStatic = var PassiveStatic = /* */ 8388608; -var SuspenseyCommit = - /* */ +var MaySuspendCommit = + /* */ 16777216; // Flag used to identify newly inserted fibers. It isn't reset after commit unlike `Placement`. var PlacementDEV = @@ -594,7 +595,7 @@ var PassiveMask = Passive$1 | Visibility | ChildDeletion; // Union of tags that // This allows certain concepts to persist without recalculating them, // e.g. whether a subtree contains passive effects or portals. -var StaticMask = LayoutStatic | PassiveStatic | RefStatic | SuspenseyCommit; +var StaticMask = LayoutStatic | PassiveStatic | RefStatic | MaySuspendCommit; var ReactCurrentOwner$3 = ReactSharedInternals.ReactCurrentOwner; function getNearestMountedFiber(fiber) { @@ -11464,6 +11465,7 @@ function preinit$1(href, options) { link.addEventListener("error", function () { state.loading |= Errored; }); + state.loading |= Inserted; insertStylesheet(_instance, precedence, resourceRoot); } // Construct a Resource and cache it @@ -11795,7 +11797,11 @@ function acquireResource(hoistableRoot, resource, props) { var ownerDocument = getDocumentFromRoot(hoistableRoot); instance = ownerDocument.createElement("style"); markNodeAsHoistable(instance); - setInitialProperties(instance, "style", styleProps); + setInitialProperties(instance, "style", styleProps); // TODO: `style` does not have loading state for tracking insertions. I + // guess because these aren't suspensey? Not sure whether this is a + // factoring smell. + // resource.state.loading |= Inserted; + insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot); resource.instance = instance; return instance; @@ -11836,6 +11842,7 @@ function acquireResource(hoistableRoot, resource, props) { linkInstance.onerror = reject; }); setInitialProperties(_instance3, "link", stylesheetProps); + resource.state.loading |= Inserted; insertStylesheet(_instance3, _qualifiedProps.precedence, hoistableRoot); resource.instance = _instance3; return _instance3; @@ -11892,6 +11899,28 @@ function acquireResource(hoistableRoot, resource, props) { ); } } + } else { + // In the case of stylesheets, they might have already been assigned an + // instance during `suspendResource`. But that doesn't mean they were + // inserted, because the commit might have been interrupted. So we need to + // check now. + // + // The other resource types are unaffected because they are not + // yet suspensey. + // + // TODO: This is a bit of a code smell. Consider refactoring how + // `suspendResource` and `acquireResource` work together. The idea is that + // `suspendResource` does all the same stuff as `acquireResource` except + // for the insertion. + if ( + resource.type === "stylesheet" && + (resource.state.loading & Inserted) === NotLoaded + ) { + var _qualifiedProps2 = props; + var _instance5 = resource.instance; + resource.state.loading |= Inserted; + insertStylesheet(_instance5, _qualifiedProps2.precedence, hoistableRoot); + } } return resource.instance; @@ -12339,9 +12368,6 @@ function isHostHoistableType(type, props, hostContext) { return false; } -function maySuspendCommit(type, props) { - return false; -} function mayResourceSuspendCommit(resource) { return ( resource.type === "stylesheet" && @@ -12352,6 +12378,17 @@ function preloadInstance(type, props) { // Return true to indicate it's already loaded return true; } +function preloadResource(resource) { + if ( + resource.type === "stylesheet" && + (resource.state.loading & Settled) === NotLoaded + ) { + // we have not finished loading the underlying stylesheet yet. + return false; + } // Return true to indicate it's already loaded + + return true; +} var suspendedState = null; // We use a noop function when we begin suspending because if possible we want the // waitfor step to finish synchronously. If it doesn't we'll return a function to // provide the actual unsuspend function and that will get completed when the count @@ -28454,7 +28491,11 @@ function updateHostComponent( markUpdate(workInProgress); } } -} // TODO: This should ideally move to begin phase, but currently the instance is +} // This function must be called at the very end of the complete phase, because +// it might throw to suspend, and if the resource immediately loads, the work +// loop will resume rendering as if the work-in-progress completed. So it must +// fully complete. +// TODO: This should ideally move to begin phase, but currently the instance is // not created until the complete phase. For our existing use cases, host nodes // that suspend don't have children, so it doesn't matter. But that might not // always be true in the future. @@ -28465,24 +28506,42 @@ function preloadInstanceAndSuspendIfNeeded( props, renderLanes ) { - workInProgress.flags |= SuspenseyCommit; // Check if we're rendering at a "non-urgent" priority. This is the same - // check that `useDeferredValue` does to determine whether it needs to - // defer. This is partly for gradual adoption purposes (i.e. shouldn't start - // suspending until you opt in with startTransition or Suspense) but it - // also happens to be the desired behavior for the concrete use cases we've - // thought of so far, like CSS loading, fonts, images, etc. - // TODO: We may decide to expose a way to force a fallback even during a - // sync update. + { + // If this flag was set previously, we can remove it. The flag + // represents whether this particular set of props might ever need to + // suspend. The safest thing to do is for maySuspendCommit to always + // return true, but if the renderer is reasonably confident that the + // underlying resource won't be evicted, it can return false as a + // performance optimization. + workInProgress.flags &= ~MaySuspendCommit; + return; + } // Mark this fiber with a flag. This gets set on all host instances +} - if (!includesOnlyNonUrgentLanes(renderLanes)); +function preloadResourceAndSuspendIfNeeded( + workInProgress, + resource, + type, + props, + renderLanes +) { + // This is a fork of preloadInstanceAndSuspendIfNeeded, but for resources. + if (!mayResourceSuspendCommit(resource)) { + workInProgress.flags &= ~MaySuspendCommit; + return; + } + + workInProgress.flags |= MaySuspendCommit; + var rootRenderLanes = getWorkInProgressRootRenderLanes(); + + if (!includesOnlyNonUrgentLanes(rootRenderLanes)); else { - // Preload the instance - var isReady = preloadInstance(); + var isReady = preloadResource(resource); if (!isReady) { - if (shouldRemainOnPreviousScreen()); - else { - // Trigger a fallback rather than block the render. + if (shouldRemainOnPreviousScreen()) { + workInProgress.flags |= ShouldSuspendCommit; + } else { suspendCommit(); } } @@ -28937,58 +28996,74 @@ function completeWork(current, workInProgress, renderLanes) { case HostHoistable: { { - var currentRef = current ? current.ref : null; + // The branching here is more complicated than you might expect because + // a HostHoistable sometimes corresponds to a Resource and sometimes + // corresponds to an Instance. It can also switch during an update. + var type = workInProgress.type; + var nextResource = workInProgress.memoizedState; - if (currentRef !== workInProgress.ref) { - markRef(workInProgress); - } - - var maySuspend = false; // @TODO refactor this block to create the instance here in complete phase if we - // are not hydrating. - - if ( + if (current === null) { // We are mounting and must Update this Hoistable in this commit - current === null || // We are transitioning to, from, or between Hoistable Resources - // and require an update - current.memoizedState !== workInProgress.memoizedState - ) { - if (workInProgress.memoizedState !== null) { - maySuspend = mayResourceSuspendCommit(workInProgress.memoizedState); - } else { - maySuspend = maySuspendCommit(); + // @TODO refactor this block to create the instance here in complete + // phase if we are not hydrating. + markUpdate(workInProgress); + + if (workInProgress.ref !== null) { + markRef(workInProgress); } - markUpdate(workInProgress); - } else if (workInProgress.memoizedState === null) { - maySuspend = maySuspendCommit(); // We may have props to update on the Hoistable instance. We use the - // updateHostComponent path becuase it produces the update queue - // we need for Hoistables - - updateHostComponent( - current, - workInProgress, - workInProgress.type, - workInProgress.pendingProps - ); - } - - bubbleProperties(workInProgress); // This must come at the very end of the complete phase, because it might - // throw to suspend, and if the resource immediately loads, the work loop - // will resume rendering as if the work-in-progress completed. So it must - // fully complete. - - if (maySuspend) { - preloadInstanceAndSuspendIfNeeded( - workInProgress, - workInProgress.type, - workInProgress.pendingProps, - renderLanes - ); + if (nextResource !== null) { + // This is a Hoistable Resource + // This must come at the very end of the complete phase. + bubbleProperties(workInProgress); + preloadResourceAndSuspendIfNeeded(workInProgress, nextResource); + return null; + } else { + // This is a Hoistable Instance + // This must come at the very end of the complete phase. + bubbleProperties(workInProgress); + preloadInstanceAndSuspendIfNeeded(workInProgress); + return null; + } } else { - workInProgress.flags &= ~SuspenseyCommit; - } + // We are updating. + var currentResource = current.memoizedState; - return null; + if (nextResource !== currentResource) { + // We are transitioning to, from, or between Hoistable Resources + // and require an update + markUpdate(workInProgress); + } + + if (current.ref !== workInProgress.ref) { + markRef(workInProgress); + } + + if (nextResource !== null) { + // This is a Hoistable Resource + // This must come at the very end of the complete phase. + bubbleProperties(workInProgress); + + if (nextResource === currentResource) { + workInProgress.flags &= ~MaySuspendCommit; + } else { + preloadResourceAndSuspendIfNeeded(workInProgress, nextResource); + } + + return null; + } else { + // This is a Hoistable Instance + // + // We may have props to update on the Hoistable instance. We use the + // updateHostComponent path becuase it produces the update queue + // we need for Hoistables. + updateHostComponent(current, workInProgress, type, newProps); // This must come at the very end of the complete phase. + + bubbleProperties(workInProgress); + preloadInstanceAndSuspendIfNeeded(workInProgress); + return null; + } + } } } // eslint-disable-next-line-no-fallthrough @@ -28997,10 +29072,10 @@ function completeWork(current, workInProgress, renderLanes) { { popHostContext(workInProgress); var rootContainerInstance = getRootHostContainer(); - var type = workInProgress.type; + var _type = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { - updateHostComponent(current, workInProgress, type, newProps); + updateHostComponent(current, workInProgress, _type, newProps); if (current.ref !== workInProgress.ref) { markRef(workInProgress); @@ -29032,7 +29107,7 @@ function completeWork(current, workInProgress, renderLanes) { instance = workInProgress.stateNode; } else { instance = resolveSingletonInstance( - type, + _type, newProps, rootContainerInstance, currentHostContext, @@ -29056,12 +29131,10 @@ function completeWork(current, workInProgress, renderLanes) { case HostComponent: { popHostContext(workInProgress); - var _type = workInProgress.type; - - var _maySuspend = maySuspendCommit(); + var _type2 = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { - updateHostComponent(current, workInProgress, _type, newProps); + updateHostComponent(current, workInProgress, _type2, newProps); if (current.ref !== workInProgress.ref) { markRef(workInProgress); @@ -29100,7 +29173,7 @@ function completeWork(current, workInProgress, renderLanes) { var _rootContainerInstance = getRootHostContainer(); var _instance3 = createInstance( - _type, + _type2, newProps, _rootContainerInstance, _currentHostContext2, @@ -29112,7 +29185,7 @@ function completeWork(current, workInProgress, renderLanes) { // (eg DOM renderer supports auto-focus for certain elements). // Make sure such renderers get scheduled for later work. - if (finalizeInitialChildren(_instance3, _type, newProps)) { + if (finalizeInitialChildren(_instance3, _type2, newProps)) { markUpdate(workInProgress); } } @@ -29128,17 +29201,7 @@ function completeWork(current, workInProgress, renderLanes) { // will resume rendering as if the work-in-progress completed. So it must // fully complete. - if (_maySuspend) { - preloadInstanceAndSuspendIfNeeded( - workInProgress, - _type, - newProps, - renderLanes - ); - } else { - workInProgress.flags &= ~SuspenseyCommit; - } - + preloadInstanceAndSuspendIfNeeded(workInProgress); return null; } @@ -33878,13 +33941,24 @@ function commitPassiveUnmountEffects(finishedWork) { setCurrentFiber(finishedWork); commitPassiveUnmountOnFiber(finishedWork); resetCurrentFiber(); -} +} // If we're inside a brand new tree, or a tree that was already visible, then we +// should only suspend host components that have a ShouldSuspendCommit flag. +// Components without it haven't changed since the last commit, so we can skip +// over those. +// +// When we enter a tree that is being revealed (going from hidden -> visible), +// we need to suspend _any_ component that _may_ suspend. Even if they're +// already in the "current" tree. Because their visibility has changed, the +// browser may not have prerendered them yet. So we check the MaySuspendCommit +// flag instead. + +var suspenseyCommitFlag = ShouldSuspendCommit; function accumulateSuspenseyCommit(finishedWork) { accumulateSuspenseyCommitOnFiber(finishedWork); } function recursivelyAccumulateSuspenseyCommit(parentFiber) { - if (parentFiber.subtreeFlags & SuspenseyCommit) { + if (parentFiber.subtreeFlags & suspenseyCommitFlag) { var child = parentFiber.child; while (child !== null) { @@ -33899,7 +33973,7 @@ function accumulateSuspenseyCommitOnFiber(fiber) { case HostHoistable: { recursivelyAccumulateSuspenseyCommit(fiber); - if (fiber.flags & SuspenseyCommit) { + if (fiber.flags & suspenseyCommitFlag) { if (fiber.memoizedState !== null) { suspendResource( // This should always be set by visiting HostRoot first @@ -33927,10 +34001,33 @@ function accumulateSuspenseyCommitOnFiber(fiber) { currentHoistableRoot = getHoistableRoot(container); recursivelyAccumulateSuspenseyCommit(fiber); currentHoistableRoot = previousHoistableRoot; - break; } + + break; + } + + case OffscreenComponent: { + var isHidden = fiber.memoizedState !== null; + + if (isHidden); + else { + var current = fiber.alternate; + var wasHidden = current !== null && current.memoizedState !== null; + + if (wasHidden) { + // This tree is being revealed. Visit all newly visible suspensey + // instances, even if they're in the current tree. + var prevFlags = suspenseyCommitFlag; + suspenseyCommitFlag = MaySuspendCommit; + recursivelyAccumulateSuspenseyCommit(fiber); + suspenseyCommitFlag = prevFlags; + } else { + recursivelyAccumulateSuspenseyCommit(fiber); + } + } + + break; } - // eslint-disable-next-line-no-fallthrough default: { recursivelyAccumulateSuspenseyCommit(fiber); @@ -36098,15 +36195,24 @@ function shouldRemainOnPreviousScreen() { if (handler === null); else { - if (includesOnlyRetries(workInProgressRootRenderLanes)) { + if ( + includesOnlyRetries(workInProgressRootRenderLanes) || // In this context, an OffscreenLane counts as a Retry + // TODO: It's become increasingly clear that Retries and Offscreen are + // deeply connected. They probably can be unified further. + includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) + ) { // During a retry, we can suspend rendering if the nearest Suspense boundary // is the boundary of the "shell", because we're guaranteed not to block // any new content from appearing. + // + // The reason we must check if this is a retry is because it guarantees + // that suspending the work loop won't block an actual update, because + // retries don't "update" anything; they fill in fallbacks that were left + // behind by a previous transition. return handler === getShellBoundary(); } } // For all other Lanes besides Transitions and Retries, we should not wait // for the data to load. - // TODO: We should wait during Offscreen prerendering, too. return false; } @@ -39564,7 +39670,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-30e8fd15"; +var ReactVersion = "18.3.0-www-classic-f9e1fdbb"; function createPortal$1( children, diff --git a/compiled/facebook-www/ReactDOM-dev.modern.js b/compiled/facebook-www/ReactDOM-dev.modern.js index 0fe351f3ab..d92efb1456 100644 --- a/compiled/facebook-www/ReactDOM-dev.modern.js +++ b/compiled/facebook-www/ReactDOM-dev.modern.js @@ -330,11 +330,12 @@ var Visibility = 8192; var StoreConsistency = /* */ - 16384; // It's OK to reuse this bit because these flags are mutually exclusive for + 16384; // It's OK to reuse these bits because these flags are mutually exclusive for // different fiber types. We should really be doing this for as many flags as // possible, because we're about to run out of bits. var ScheduleRetry = StoreConsistency; +var ShouldSuspendCommit = Visibility; var LifecycleEffectMask = Passive$1 | Update | Callback | Ref | Snapshot | StoreConsistency; // Union of all commit flags (flags with the lifetime of a particular commit) @@ -374,8 +375,8 @@ var LayoutStatic = var PassiveStatic = /* */ 8388608; -var SuspenseyCommit = - /* */ +var MaySuspendCommit = + /* */ 16777216; // Flag used to identify newly inserted fibers. It isn't reset after commit unlike `Placement`. var PlacementDEV = @@ -411,7 +412,7 @@ var PassiveMask = Passive$1 | Visibility | ChildDeletion; // Union of tags that // This allows certain concepts to persist without recalculating them, // e.g. whether a subtree contains passive effects or portals. -var StaticMask = LayoutStatic | PassiveStatic | RefStatic | SuspenseyCommit; +var StaticMask = LayoutStatic | PassiveStatic | RefStatic | MaySuspendCommit; // This module only exists as an ESM wrapper around the external CommonJS var scheduleCallback$2 = Scheduler.unstable_scheduleCallback; @@ -22558,7 +22559,11 @@ function updateHostComponent( markUpdate(workInProgress); } } -} // TODO: This should ideally move to begin phase, but currently the instance is +} // This function must be called at the very end of the complete phase, because +// it might throw to suspend, and if the resource immediately loads, the work +// loop will resume rendering as if the work-in-progress completed. So it must +// fully complete. +// TODO: This should ideally move to begin phase, but currently the instance is // not created until the complete phase. For our existing use cases, host nodes // that suspend don't have children, so it doesn't matter. But that might not // always be true in the future. @@ -22569,24 +22574,42 @@ function preloadInstanceAndSuspendIfNeeded( props, renderLanes ) { - workInProgress.flags |= SuspenseyCommit; // Check if we're rendering at a "non-urgent" priority. This is the same - // check that `useDeferredValue` does to determine whether it needs to - // defer. This is partly for gradual adoption purposes (i.e. shouldn't start - // suspending until you opt in with startTransition or Suspense) but it - // also happens to be the desired behavior for the concrete use cases we've - // thought of so far, like CSS loading, fonts, images, etc. - // TODO: We may decide to expose a way to force a fallback even during a - // sync update. + { + // If this flag was set previously, we can remove it. The flag + // represents whether this particular set of props might ever need to + // suspend. The safest thing to do is for maySuspendCommit to always + // return true, but if the renderer is reasonably confident that the + // underlying resource won't be evicted, it can return false as a + // performance optimization. + workInProgress.flags &= ~MaySuspendCommit; + return; + } // Mark this fiber with a flag. This gets set on all host instances +} - if (!includesOnlyNonUrgentLanes(renderLanes)); +function preloadResourceAndSuspendIfNeeded( + workInProgress, + resource, + type, + props, + renderLanes +) { + // This is a fork of preloadInstanceAndSuspendIfNeeded, but for resources. + if (!mayResourceSuspendCommit(resource)) { + workInProgress.flags &= ~MaySuspendCommit; + return; + } + + workInProgress.flags |= MaySuspendCommit; + var rootRenderLanes = getWorkInProgressRootRenderLanes(); + + if (!includesOnlyNonUrgentLanes(rootRenderLanes)); else { - // Preload the instance - var isReady = preloadInstance(); + var isReady = preloadResource(resource); if (!isReady) { - if (shouldRemainOnPreviousScreen()); - else { - // Trigger a fallback rather than block the render. + if (shouldRemainOnPreviousScreen()) { + workInProgress.flags |= ShouldSuspendCommit; + } else { suspendCommit(); } } @@ -23034,58 +23057,74 @@ function completeWork(current, workInProgress, renderLanes) { case HostHoistable: { { - var currentRef = current ? current.ref : null; + // The branching here is more complicated than you might expect because + // a HostHoistable sometimes corresponds to a Resource and sometimes + // corresponds to an Instance. It can also switch during an update. + var type = workInProgress.type; + var nextResource = workInProgress.memoizedState; - if (currentRef !== workInProgress.ref) { - markRef(workInProgress); - } - - var maySuspend = false; // @TODO refactor this block to create the instance here in complete phase if we - // are not hydrating. - - if ( + if (current === null) { // We are mounting and must Update this Hoistable in this commit - current === null || // We are transitioning to, from, or between Hoistable Resources - // and require an update - current.memoizedState !== workInProgress.memoizedState - ) { - if (workInProgress.memoizedState !== null) { - maySuspend = mayResourceSuspendCommit(workInProgress.memoizedState); - } else { - maySuspend = maySuspendCommit(); + // @TODO refactor this block to create the instance here in complete + // phase if we are not hydrating. + markUpdate(workInProgress); + + if (workInProgress.ref !== null) { + markRef(workInProgress); } - markUpdate(workInProgress); - } else if (workInProgress.memoizedState === null) { - maySuspend = maySuspendCommit(); // We may have props to update on the Hoistable instance. We use the - // updateHostComponent path becuase it produces the update queue - // we need for Hoistables - - updateHostComponent( - current, - workInProgress, - workInProgress.type, - workInProgress.pendingProps - ); - } - - bubbleProperties(workInProgress); // This must come at the very end of the complete phase, because it might - // throw to suspend, and if the resource immediately loads, the work loop - // will resume rendering as if the work-in-progress completed. So it must - // fully complete. - - if (maySuspend) { - preloadInstanceAndSuspendIfNeeded( - workInProgress, - workInProgress.type, - workInProgress.pendingProps, - renderLanes - ); + if (nextResource !== null) { + // This is a Hoistable Resource + // This must come at the very end of the complete phase. + bubbleProperties(workInProgress); + preloadResourceAndSuspendIfNeeded(workInProgress, nextResource); + return null; + } else { + // This is a Hoistable Instance + // This must come at the very end of the complete phase. + bubbleProperties(workInProgress); + preloadInstanceAndSuspendIfNeeded(workInProgress); + return null; + } } else { - workInProgress.flags &= ~SuspenseyCommit; - } + // We are updating. + var currentResource = current.memoizedState; - return null; + if (nextResource !== currentResource) { + // We are transitioning to, from, or between Hoistable Resources + // and require an update + markUpdate(workInProgress); + } + + if (current.ref !== workInProgress.ref) { + markRef(workInProgress); + } + + if (nextResource !== null) { + // This is a Hoistable Resource + // This must come at the very end of the complete phase. + bubbleProperties(workInProgress); + + if (nextResource === currentResource) { + workInProgress.flags &= ~MaySuspendCommit; + } else { + preloadResourceAndSuspendIfNeeded(workInProgress, nextResource); + } + + return null; + } else { + // This is a Hoistable Instance + // + // We may have props to update on the Hoistable instance. We use the + // updateHostComponent path becuase it produces the update queue + // we need for Hoistables. + updateHostComponent(current, workInProgress, type, newProps); // This must come at the very end of the complete phase. + + bubbleProperties(workInProgress); + preloadInstanceAndSuspendIfNeeded(workInProgress); + return null; + } + } } } // eslint-disable-next-line-no-fallthrough @@ -23094,10 +23133,10 @@ function completeWork(current, workInProgress, renderLanes) { { popHostContext(workInProgress); var rootContainerInstance = getRootHostContainer(); - var type = workInProgress.type; + var _type = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { - updateHostComponent(current, workInProgress, type, newProps); + updateHostComponent(current, workInProgress, _type, newProps); if (current.ref !== workInProgress.ref) { markRef(workInProgress); @@ -23129,7 +23168,7 @@ function completeWork(current, workInProgress, renderLanes) { instance = workInProgress.stateNode; } else { instance = resolveSingletonInstance( - type, + _type, newProps, rootContainerInstance, currentHostContext, @@ -23153,12 +23192,10 @@ function completeWork(current, workInProgress, renderLanes) { case HostComponent: { popHostContext(workInProgress); - var _type = workInProgress.type; - - var _maySuspend = maySuspendCommit(); + var _type2 = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { - updateHostComponent(current, workInProgress, _type, newProps); + updateHostComponent(current, workInProgress, _type2, newProps); if (current.ref !== workInProgress.ref) { markRef(workInProgress); @@ -23197,7 +23234,7 @@ function completeWork(current, workInProgress, renderLanes) { var _rootContainerInstance = getRootHostContainer(); var _instance3 = createInstance( - _type, + _type2, newProps, _rootContainerInstance, _currentHostContext2, @@ -23209,7 +23246,7 @@ function completeWork(current, workInProgress, renderLanes) { // (eg DOM renderer supports auto-focus for certain elements). // Make sure such renderers get scheduled for later work. - if (finalizeInitialChildren(_instance3, _type, newProps)) { + if (finalizeInitialChildren(_instance3, _type2, newProps)) { markUpdate(workInProgress); } } @@ -23225,17 +23262,7 @@ function completeWork(current, workInProgress, renderLanes) { // will resume rendering as if the work-in-progress completed. So it must // fully complete. - if (_maySuspend) { - preloadInstanceAndSuspendIfNeeded( - workInProgress, - _type, - newProps, - renderLanes - ); - } else { - workInProgress.flags &= ~SuspenseyCommit; - } - + preloadInstanceAndSuspendIfNeeded(workInProgress); return null; } @@ -27953,13 +27980,24 @@ function commitPassiveUnmountEffects(finishedWork) { setCurrentFiber(finishedWork); commitPassiveUnmountOnFiber(finishedWork); resetCurrentFiber(); -} +} // If we're inside a brand new tree, or a tree that was already visible, then we +// should only suspend host components that have a ShouldSuspendCommit flag. +// Components without it haven't changed since the last commit, so we can skip +// over those. +// +// When we enter a tree that is being revealed (going from hidden -> visible), +// we need to suspend _any_ component that _may_ suspend. Even if they're +// already in the "current" tree. Because their visibility has changed, the +// browser may not have prerendered them yet. So we check the MaySuspendCommit +// flag instead. + +var suspenseyCommitFlag = ShouldSuspendCommit; function accumulateSuspenseyCommit(finishedWork) { accumulateSuspenseyCommitOnFiber(finishedWork); } function recursivelyAccumulateSuspenseyCommit(parentFiber) { - if (parentFiber.subtreeFlags & SuspenseyCommit) { + if (parentFiber.subtreeFlags & suspenseyCommitFlag) { var child = parentFiber.child; while (child !== null) { @@ -27974,7 +28012,7 @@ function accumulateSuspenseyCommitOnFiber(fiber) { case HostHoistable: { recursivelyAccumulateSuspenseyCommit(fiber); - if (fiber.flags & SuspenseyCommit) { + if (fiber.flags & suspenseyCommitFlag) { if (fiber.memoizedState !== null) { suspendResource( // This should always be set by visiting HostRoot first @@ -28002,10 +28040,33 @@ function accumulateSuspenseyCommitOnFiber(fiber) { currentHoistableRoot = getHoistableRoot(container); recursivelyAccumulateSuspenseyCommit(fiber); currentHoistableRoot = previousHoistableRoot; - break; } + + break; + } + + case OffscreenComponent: { + var isHidden = fiber.memoizedState !== null; + + if (isHidden); + else { + var current = fiber.alternate; + var wasHidden = current !== null && current.memoizedState !== null; + + if (wasHidden) { + // This tree is being revealed. Visit all newly visible suspensey + // instances, even if they're in the current tree. + var prevFlags = suspenseyCommitFlag; + suspenseyCommitFlag = MaySuspendCommit; + recursivelyAccumulateSuspenseyCommit(fiber); + suspenseyCommitFlag = prevFlags; + } else { + recursivelyAccumulateSuspenseyCommit(fiber); + } + } + + break; } - // eslint-disable-next-line-no-fallthrough default: { recursivelyAccumulateSuspenseyCommit(fiber); @@ -30173,15 +30234,24 @@ function shouldRemainOnPreviousScreen() { if (handler === null); else { - if (includesOnlyRetries(workInProgressRootRenderLanes)) { + if ( + includesOnlyRetries(workInProgressRootRenderLanes) || // In this context, an OffscreenLane counts as a Retry + // TODO: It's become increasingly clear that Retries and Offscreen are + // deeply connected. They probably can be unified further. + includesSomeLane(workInProgressRootRenderLanes, OffscreenLane) + ) { // During a retry, we can suspend rendering if the nearest Suspense boundary // is the boundary of the "shell", because we're guaranteed not to block // any new content from appearing. + // + // The reason we must check if this is a retry is because it guarantees + // that suspending the work loop won't block an actual update, because + // retries don't "update" anything; they fill in fallbacks that were left + // behind by a previous transition. return handler === getShellBoundary(); } } // For all other Lanes besides Transitions and Retries, we should not wait // for the data to load. - // TODO: We should wait during Offscreen prerendering, too. return false; } @@ -33639,7 +33709,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-ef21bdef"; +var ReactVersion = "18.3.0-www-modern-68e129ab"; function createPortal$1( children, @@ -42813,6 +42883,7 @@ function preinit$1(href, options) { link.addEventListener("error", function () { state.loading |= Errored; }); + state.loading |= Inserted; insertStylesheet(_instance, precedence, resourceRoot); } // Construct a Resource and cache it @@ -43144,7 +43215,11 @@ function acquireResource(hoistableRoot, resource, props) { var ownerDocument = getDocumentFromRoot(hoistableRoot); instance = ownerDocument.createElement("style"); markNodeAsHoistable(instance); - setInitialProperties(instance, "style", styleProps); + setInitialProperties(instance, "style", styleProps); // TODO: `style` does not have loading state for tracking insertions. I + // guess because these aren't suspensey? Not sure whether this is a + // factoring smell. + // resource.state.loading |= Inserted; + insertStylesheet(instance, qualifiedProps.precedence, hoistableRoot); resource.instance = instance; return instance; @@ -43185,6 +43260,7 @@ function acquireResource(hoistableRoot, resource, props) { linkInstance.onerror = reject; }); setInitialProperties(_instance3, "link", stylesheetProps); + resource.state.loading |= Inserted; insertStylesheet(_instance3, _qualifiedProps.precedence, hoistableRoot); resource.instance = _instance3; return _instance3; @@ -43241,6 +43317,28 @@ function acquireResource(hoistableRoot, resource, props) { ); } } + } else { + // In the case of stylesheets, they might have already been assigned an + // instance during `suspendResource`. But that doesn't mean they were + // inserted, because the commit might have been interrupted. So we need to + // check now. + // + // The other resource types are unaffected because they are not + // yet suspensey. + // + // TODO: This is a bit of a code smell. Consider refactoring how + // `suspendResource` and `acquireResource` work together. The idea is that + // `suspendResource` does all the same stuff as `acquireResource` except + // for the insertion. + if ( + resource.type === "stylesheet" && + (resource.state.loading & Inserted) === NotLoaded + ) { + var _qualifiedProps2 = props; + var _instance5 = resource.instance; + resource.state.loading |= Inserted; + insertStylesheet(_instance5, _qualifiedProps2.precedence, hoistableRoot); + } } return resource.instance; @@ -43688,9 +43786,6 @@ function isHostHoistableType(type, props, hostContext) { return false; } -function maySuspendCommit(type, props) { - return false; -} function mayResourceSuspendCommit(resource) { return ( resource.type === "stylesheet" && @@ -43701,6 +43796,17 @@ function preloadInstance(type, props) { // Return true to indicate it's already loaded return true; } +function preloadResource(resource) { + if ( + resource.type === "stylesheet" && + (resource.state.loading & Settled) === NotLoaded + ) { + // we have not finished loading the underlying stylesheet yet. + return false; + } // Return true to indicate it's already loaded + + return true; +} var suspendedState = null; // We use a noop function when we begin suspending because if possible we want the // waitfor step to finish synchronously. If it doesn't we'll return a function to // provide the actual unsuspend function and that will get completed when the count diff --git a/compiled/facebook-www/ReactDOM-prod.classic.js b/compiled/facebook-www/ReactDOM-prod.classic.js index 6c85221104..8e85221ab8 100644 --- a/compiled/facebook-www/ReactDOM-prod.classic.js +++ b/compiled/facebook-www/ReactDOM-prod.classic.js @@ -2352,6 +2352,7 @@ function preinit$1(href, options) { link.addEventListener("error", function () { state.loading |= 2; }); + state.loading |= 4; insertStylesheet(resource, precedence, resourceRoot); } resource = { @@ -2567,6 +2568,7 @@ function acquireResource(hoistableRoot, resource, props) { linkInstance.onerror = reject; }); setInitialProperties(instance$33, "link", key); + resource.state.loading |= 4; insertStylesheet(instance$33, props.precedence, hoistableRoot); return (resource.instance = instance$33); case "script": @@ -2596,6 +2598,12 @@ function acquireResource(hoistableRoot, resource, props) { default: throw Error(formatProdErrorMessage(443, resource.type)); } + else + "stylesheet" === resource.type && + 0 === (resource.state.loading & 4) && + ((key = resource.instance), + (resource.state.loading |= 4), + insertStylesheet(key, props.precedence, hoistableRoot)); return resource.instance; } function insertStylesheet(instance, precedence, root) { @@ -4867,10 +4875,10 @@ createFunctionComponentUpdateQueue = function () { function use(usable) { if (null !== usable && "object" === typeof usable) { if ("function" === typeof usable.then) { - var index$73 = thenableIndexCounter; + var index$75 = thenableIndexCounter; thenableIndexCounter += 1; null === thenableState && (thenableState = []); - usable = trackUsedThenable(thenableState, usable, index$73); + usable = trackUsedThenable(thenableState, usable, index$75); null === currentlyRenderingFiber$1.alternate && (null === workInProgressHook ? null === currentlyRenderingFiber$1.memoizedState @@ -5362,16 +5370,16 @@ function refreshCache(fiber, seedKey, seedValue) { case 3: var lane = requestUpdateLane(provider); fiber = createUpdate(lane); - var root$79 = enqueueUpdate(provider, fiber, lane); - if (null !== root$79) { + var root$81 = enqueueUpdate(provider, fiber, lane); + if (null !== root$81) { var eventTime = requestEventTime(); - scheduleUpdateOnFiber(root$79, provider, lane, eventTime); - entangleTransitions(root$79, provider, lane); + scheduleUpdateOnFiber(root$81, provider, lane, eventTime); + entangleTransitions(root$81, provider, lane); } provider = createCache(); null !== seedKey && void 0 !== seedKey && - null !== root$79 && + null !== root$81 && provider.data.set(seedKey, seedValue); fiber.payload = { cache: provider }; return; @@ -5578,15 +5586,15 @@ var HooksDispatcherOnMount = { getServerSnapshot = getServerSnapshot(); } else { getServerSnapshot = getSnapshot(); - var root$75 = workInProgressRoot; - if (null === root$75) throw Error(formatProdErrorMessage(349)); - includesBlockingLane(root$75, renderLanes$1) || + var root$77 = workInProgressRoot; + if (null === root$77) throw Error(formatProdErrorMessage(349)); + includesBlockingLane(root$77, renderLanes$1) || pushStoreConsistencyCheck(fiber, getSnapshot, getServerSnapshot); } hook.memoizedState = getServerSnapshot; - root$75 = { value: getServerSnapshot, getSnapshot: getSnapshot }; - hook.queue = root$75; - mountEffect(subscribeToStore.bind(null, fiber, root$75, subscribe), [ + root$77 = { value: getServerSnapshot, getSnapshot: getSnapshot }; + hook.queue = root$77; + mountEffect(subscribeToStore.bind(null, fiber, root$77, subscribe), [ subscribe ]); fiber.flags |= 2048; @@ -5595,7 +5603,7 @@ var HooksDispatcherOnMount = { updateStoreInstance.bind( null, fiber, - root$75, + root$77, getServerSnapshot, getSnapshot ), @@ -6094,10 +6102,10 @@ var markerInstanceStack = createCursor(null); function pushRootMarkerInstance(workInProgress) { if (enableTransitionTracing) { var transitions = workInProgressTransitions, - root$90 = workInProgress.stateNode; + root$92 = workInProgress.stateNode; null !== transitions && transitions.forEach(function (transition) { - if (!root$90.incompleteTransitions.has(transition)) { + if (!root$92.incompleteTransitions.has(transition)) { var markerInstance = { tag: 0, transitions: new Set([transition]), @@ -6105,11 +6113,11 @@ function pushRootMarkerInstance(workInProgress) { aborts: null, name: null }; - root$90.incompleteTransitions.set(transition, markerInstance); + root$92.incompleteTransitions.set(transition, markerInstance); } }); var markerInstances = []; - root$90.incompleteTransitions.forEach(function (markerInstance) { + root$92.incompleteTransitions.forEach(function (markerInstance) { markerInstances.push(markerInstance); }); push(markerInstanceStack, markerInstances); @@ -6809,14 +6817,14 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { } JSCompiler_temp = current.memoizedState; if (null !== JSCompiler_temp) { - var dehydrated$97 = JSCompiler_temp.dehydrated; - if (null !== dehydrated$97) + var dehydrated$99 = JSCompiler_temp.dehydrated; + if (null !== dehydrated$99) return updateDehydratedSuspenseComponent( current, workInProgress, didSuspend, nextProps, - dehydrated$97, + dehydrated$99, JSCompiler_temp, renderLanes ); @@ -6826,7 +6834,7 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { showFallback = nextProps.fallback; didSuspend = workInProgress.mode; JSCompiler_temp = current.child; - dehydrated$97 = JSCompiler_temp.sibling; + dehydrated$99 = JSCompiler_temp.sibling; var primaryChildProps = { mode: "hidden", children: nextProps.children }; 0 === (didSuspend & 1) && workInProgress.child !== JSCompiler_temp ? ((nextProps = workInProgress.child), @@ -6835,8 +6843,8 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { (workInProgress.deletions = null)) : ((nextProps = createWorkInProgress(JSCompiler_temp, primaryChildProps)), (nextProps.subtreeFlags = JSCompiler_temp.subtreeFlags & 31457280)); - null !== dehydrated$97 - ? (showFallback = createWorkInProgress(dehydrated$97, showFallback)) + null !== dehydrated$99 + ? (showFallback = createWorkInProgress(dehydrated$99, showFallback)) : ((showFallback = createFiberFromFragment( showFallback, didSuspend, @@ -6855,10 +6863,10 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { ? (didSuspend = mountSuspenseOffscreenState(renderLanes)) : ((JSCompiler_temp = didSuspend.cachePool), null !== JSCompiler_temp - ? ((dehydrated$97 = CacheContext._currentValue), + ? ((dehydrated$99 = CacheContext._currentValue), (JSCompiler_temp = - JSCompiler_temp.parent !== dehydrated$97 - ? { parent: dehydrated$97, pool: dehydrated$97 } + JSCompiler_temp.parent !== dehydrated$99 + ? { parent: dehydrated$99, pool: dehydrated$99 } : JSCompiler_temp)) : (JSCompiler_temp = getSuspendedCache()), (didSuspend = { @@ -6872,23 +6880,23 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { ((JSCompiler_temp = enableTransitionTracing ? markerInstanceStack.current : null), - (dehydrated$97 = showFallback.updateQueue), + (dehydrated$99 = showFallback.updateQueue), (primaryChildProps = current.updateQueue), - null === dehydrated$97 + null === dehydrated$99 ? (showFallback.updateQueue = { transitions: didSuspend, markerInstances: JSCompiler_temp, retryQueue: null }) - : dehydrated$97 === primaryChildProps + : dehydrated$99 === primaryChildProps ? (showFallback.updateQueue = { transitions: didSuspend, markerInstances: JSCompiler_temp, retryQueue: null !== primaryChildProps ? primaryChildProps.retryQueue : null }) - : ((dehydrated$97.transitions = didSuspend), - (dehydrated$97.markerInstances = JSCompiler_temp)))); + : ((dehydrated$99.transitions = didSuspend), + (dehydrated$99.markerInstances = JSCompiler_temp)))); showFallback.childLanes = current.childLanes & ~renderLanes; workInProgress.memoizedState = SUSPENDED_MARKER; return nextProps; @@ -8023,6 +8031,25 @@ function updateHostComponent(current, workInProgress, type, newProps) { markUpdate(workInProgress); } } +function preloadResourceAndSuspendIfNeeded(workInProgress, resource) { + if ("stylesheet" !== resource.type || 0 !== (resource.state.loading & 4)) + workInProgress.flags &= -16777217; + else if ( + ((workInProgress.flags |= 16777216), + 0 === (workInProgressRootRenderLanes & 42) && + ((resource = + "stylesheet" === resource.type && 0 === (resource.state.loading & 3) + ? !1 + : !0), + !resource)) + ) + if (shouldRemainOnPreviousScreen()) workInProgress.flags |= 8192; + else + throw ( + ((suspendedThenable = noopSuspenseyCommitThenable), + SuspenseyCommitException) + ); +} function scheduleRetryEffect(workInProgress, retryQueue) { null !== retryQueue ? (workInProgress.flags |= 4) @@ -8046,14 +8073,14 @@ function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { break; case "collapsed": lastTailNode = renderState.tail; - for (var lastTailNode$128 = null; null !== lastTailNode; ) - null !== lastTailNode.alternate && (lastTailNode$128 = lastTailNode), + for (var lastTailNode$130 = null; null !== lastTailNode; ) + null !== lastTailNode.alternate && (lastTailNode$130 = lastTailNode), (lastTailNode = lastTailNode.sibling); - null === lastTailNode$128 + null === lastTailNode$130 ? hasRenderedATailFallback || null === renderState.tail ? (renderState.tail = null) : (renderState.tail.sibling = null) - : (lastTailNode$128.sibling = null); + : (lastTailNode$130.sibling = null); } } function bubbleProperties(completedWork) { @@ -8063,19 +8090,19 @@ function bubbleProperties(completedWork) { newChildLanes = 0, subtreeFlags = 0; if (didBailout) - for (var child$129 = completedWork.child; null !== child$129; ) - (newChildLanes |= child$129.lanes | child$129.childLanes), - (subtreeFlags |= child$129.subtreeFlags & 31457280), - (subtreeFlags |= child$129.flags & 31457280), - (child$129.return = completedWork), - (child$129 = child$129.sibling); + for (var child$131 = completedWork.child; null !== child$131; ) + (newChildLanes |= child$131.lanes | child$131.childLanes), + (subtreeFlags |= child$131.subtreeFlags & 31457280), + (subtreeFlags |= child$131.flags & 31457280), + (child$131.return = completedWork), + (child$131 = child$131.sibling); else - for (child$129 = completedWork.child; null !== child$129; ) - (newChildLanes |= child$129.lanes | child$129.childLanes), - (subtreeFlags |= child$129.subtreeFlags), - (subtreeFlags |= child$129.flags), - (child$129.return = completedWork), - (child$129 = child$129.sibling); + for (child$131 = completedWork.child; null !== child$131; ) + (newChildLanes |= child$131.lanes | child$131.childLanes), + (subtreeFlags |= child$131.subtreeFlags), + (subtreeFlags |= child$131.flags), + (child$131.return = completedWork), + (child$131 = child$131.sibling); completedWork.subtreeFlags |= subtreeFlags; completedWork.childLanes = newChildLanes; return didBailout; @@ -8102,13 +8129,13 @@ function completeWork(current, workInProgress, renderLanes) { null ); case 3: - renderLanes = workInProgress.stateNode; + newProps = workInProgress.stateNode; enableTransitionTracing && null !== workInProgressTransitions && (workInProgress.flags |= 2048); - newProps = null; - null !== current && (newProps = current.memoizedState.cache); - workInProgress.memoizedState.cache !== newProps && + renderLanes = null; + null !== current && (renderLanes = current.memoizedState.cache); + workInProgress.memoizedState.cache !== renderLanes && (workInProgress.flags |= 2048); popProvider(CacheContext); enableTransitionTracing && @@ -8119,9 +8146,9 @@ function completeWork(current, workInProgress, renderLanes) { pop(didPerformWorkStackCursor); pop(contextStackCursor); resetWorkInProgressVersions(); - renderLanes.pendingContext && - ((renderLanes.context = renderLanes.pendingContext), - (renderLanes.pendingContext = null)); + newProps.pendingContext && + ((newProps.context = newProps.pendingContext), + (newProps.pendingContext = null)); if (null === current || null === current.child) popHydrationState(workInProgress) ? markUpdate(workInProgress) @@ -8138,39 +8165,41 @@ function completeWork(current, workInProgress, renderLanes) { (workInProgress.flags |= 2048); return null; case 26: - return ( - (current ? current.ref : null) !== workInProgress.ref && - markRef(workInProgress), - (renderLanes = !1), - null === current || - current.memoizedState !== workInProgress.memoizedState - ? (null !== workInProgress.memoizedState - ? ((current = workInProgress.memoizedState), - (renderLanes = - "stylesheet" === current.type && - 0 === (current.state.loading & 4))) - : (renderLanes = !1), - markUpdate(workInProgress)) - : null === workInProgress.memoizedState && - ((renderLanes = !1), - updateHostComponent( + renderLanes = workInProgress.type; + var nextResource = workInProgress.memoizedState; + if (null === current) + markUpdate(workInProgress), + null !== workInProgress.ref && markRef(workInProgress), + null !== nextResource + ? (bubbleProperties(workInProgress), + preloadResourceAndSuspendIfNeeded(workInProgress, nextResource)) + : (bubbleProperties(workInProgress), + (workInProgress.flags &= -16777217)); + else { + var currentResource = current.memoizedState; + nextResource !== currentResource && markUpdate(workInProgress); + current.ref !== workInProgress.ref && markRef(workInProgress); + null !== nextResource + ? (bubbleProperties(workInProgress), + nextResource === currentResource + ? (workInProgress.flags &= -16777217) + : preloadResourceAndSuspendIfNeeded(workInProgress, nextResource)) + : (updateHostComponent( current, workInProgress, - workInProgress.type, - workInProgress.pendingProps - )), - bubbleProperties(workInProgress), - (workInProgress.flags = renderLanes - ? workInProgress.flags | 16777216 - : workInProgress.flags & -16777217), - null - ); + renderLanes, + newProps + ), + bubbleProperties(workInProgress), + (workInProgress.flags &= -16777217)); + } + return null; case 27: popHostContext(workInProgress); renderLanes = rootInstanceStackCursor.current; - var type = workInProgress.type; + nextResource = workInProgress.type; if (null !== current && null != workInProgress.stateNode) - updateHostComponent(current, workInProgress, type, newProps), + updateHostComponent(current, workInProgress, nextResource, newProps), current.ref !== workInProgress.ref && markRef(workInProgress); else { if (!newProps) { @@ -8182,7 +8211,11 @@ function completeWork(current, workInProgress, renderLanes) { current = contextStackCursor$1.current; popHydrationState(workInProgress) ? prepareToHydrateHostInstance(workInProgress, current) - : ((current = resolveSingletonInstance(type, newProps, renderLanes)), + : ((current = resolveSingletonInstance( + nextResource, + newProps, + renderLanes + )), (workInProgress.stateNode = current), markUpdate(workInProgress)); null !== workInProgress.ref && markRef(workInProgress); @@ -8207,64 +8240,81 @@ function completeWork(current, workInProgress, renderLanes) { prepareToHydrateHostInstance(workInProgress, current) && markUpdate(workInProgress); else { - type = getOwnerDocumentFromRootContainer( + nextResource = getOwnerDocumentFromRootContainer( rootInstanceStackCursor.current ); switch (current) { case "http://www.w3.org/2000/svg": case "http://www.w3.org/1998/Math/MathML": - current = type.createElementNS(current, renderLanes); + current = nextResource.createElementNS(current, renderLanes); break; default: switch (renderLanes) { case "svg": - current = type.createElementNS( + current = nextResource.createElementNS( "http://www.w3.org/2000/svg", renderLanes ); break; case "math": - current = type.createElementNS( + current = nextResource.createElementNS( "http://www.w3.org/1998/Math/MathML", renderLanes ); break; case "script": - current = type.createElement("div"); + current = nextResource.createElement("div"); current.innerHTML = "