diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 5f6e39d741a18..8cfe13c3228f5 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -84a0a171ea0ecd25e287bd3d3dd30e932beb4677 +7efa9e59707b341f10fab79724e0fca373187925 diff --git a/compiled/facebook-www/REVISION_TRANSFORMS b/compiled/facebook-www/REVISION_TRANSFORMS index 5f6e39d741a18..8cfe13c3228f5 100644 --- a/compiled/facebook-www/REVISION_TRANSFORMS +++ b/compiled/facebook-www/REVISION_TRANSFORMS @@ -1 +1 @@ -84a0a171ea0ecd25e287bd3d3dd30e932beb4677 +7efa9e59707b341f10fab79724e0fca373187925 diff --git a/compiled/facebook-www/React-dev.classic.js b/compiled/facebook-www/React-dev.classic.js index 0c0001c1582f5..9acb7991d30f3 100644 --- a/compiled/facebook-www/React-dev.classic.js +++ b/compiled/facebook-www/React-dev.classic.js @@ -27,7 +27,7 @@ if ( } "use strict"; -var ReactVersion = "18.3.0-www-classic-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-classic-7efa9e597-20221215"; // ATTENTION // When adding new symbols to this file, diff --git a/compiled/facebook-www/React-dev.modern.js b/compiled/facebook-www/React-dev.modern.js index 58e1939fd0619..3c9bd9d128da1 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-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-modern-7efa9e597-20221215"; // ATTENTION // When adding new symbols to this file, diff --git a/compiled/facebook-www/React-prod.classic.js b/compiled/facebook-www/React-prod.classic.js index 42428ea9b6beb..8f918c3d0bca5 100644 --- a/compiled/facebook-www/React-prod.classic.js +++ b/compiled/facebook-www/React-prod.classic.js @@ -643,4 +643,4 @@ exports.useSyncExternalStore = function( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-www-classic-84a0a171e-20221214"; +exports.version = "18.3.0-www-classic-7efa9e597-20221215"; diff --git a/compiled/facebook-www/React-prod.modern.js b/compiled/facebook-www/React-prod.modern.js index 3bacf6e9606bb..e9b9097b0d7aa 100644 --- a/compiled/facebook-www/React-prod.modern.js +++ b/compiled/facebook-www/React-prod.modern.js @@ -635,4 +635,4 @@ exports.useSyncExternalStore = function( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-www-modern-84a0a171e-20221214"; +exports.version = "18.3.0-www-modern-7efa9e597-20221215"; diff --git a/compiled/facebook-www/React-profiling.classic.js b/compiled/facebook-www/React-profiling.classic.js index c8865de7c9548..585d434f798d8 100644 --- a/compiled/facebook-www/React-profiling.classic.js +++ b/compiled/facebook-www/React-profiling.classic.js @@ -654,7 +654,7 @@ exports.useSyncExternalStore = function( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-www-classic-84a0a171e-20221214"; +exports.version = "18.3.0-www-classic-7efa9e597-20221215"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( diff --git a/compiled/facebook-www/React-profiling.modern.js b/compiled/facebook-www/React-profiling.modern.js index 8e9e9332527ff..38097fede5bba 100644 --- a/compiled/facebook-www/React-profiling.modern.js +++ b/compiled/facebook-www/React-profiling.modern.js @@ -646,7 +646,7 @@ exports.useSyncExternalStore = function( ); }; exports.useTransition = useTransition; -exports.version = "18.3.0-www-modern-84a0a171e-20221214"; +exports.version = "18.3.0-www-modern-7efa9e597-20221215"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( diff --git a/compiled/facebook-www/ReactART-dev.classic.js b/compiled/facebook-www/ReactART-dev.classic.js index 8c87cd07a9f31..ef77756ab06ac 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-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-classic-7efa9e597-20221215"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -12716,7 +12716,14 @@ function getMarkerInstances() { return null; } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows +// into a dehydrated boundary. + +var SelectiveHydrationException = new Error( + "This is not a real error. It's an implementation detail of React's " + + "selective hydration feature. If this leaks into userspace, it's a bug in " + + "React. Please file an issue." +); var didReceiveUpdate = false; var didWarnAboutBadClass; var didWarnAboutModulePatternComponent; @@ -14911,18 +14918,29 @@ function updateDehydratedSuspenseComponent( current, attemptHydrationAtLane, eventTime - ); - } - } // If we have scheduled higher pri work above, this will just abort the render - // since we now have higher priority work. We'll try to infinitely suspend until - // we yield. TODO: We could probably just force yielding earlier instead. + ); // Throw a special object that signals to the work loop that it should + // interrupt the current render. + // + // Because we're inside a React-only execution stack, we don't + // strictly need to throw here — we could instead modify some internal + // work loop state. But using an exception means we don't need to + // check for this case on every iteration of the work loop. So doing + // it this way moves the check out of the fast path. - renderDidSuspendDelayIfPossible(); // If we rendered synchronously, we won't yield so have to render something. - // This will cause us to delete any existing content. + throw SelectiveHydrationException; + } + } // If we did not selectively hydrate, we'll continue rendering without + // hydrating. Mark this tree as suspended to prevent it from committing + // outside a transition. + // + // This path should only happen if the hydration lane already suspended. + // Currently, it also happens during sync updates because there is no + // hydration lane for sync updates. // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. + renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( current, workInProgress, @@ -22920,7 +22938,8 @@ var SuspendedOnError = 1; var SuspendedOnData = 2; var SuspendedOnImmediate = 3; var SuspendedOnDeprecatedThrowPromise = 4; -var SuspendedAndReadyToUnwind = 5; // When this is true, the work-in-progress fiber just suspended (or errored) and +var SuspendedAndReadyToUnwind = 5; +var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and // we've yet to unwind the stack. In some cases, we may yield to the main thread // after this happens. If the fiber is pinged before we resume, we can retry // immediately instead of unwinding the stack. @@ -24114,6 +24133,30 @@ function getRenderLanes() { return renderLanes$1; } +function resetWorkInProgressStack() { + if (workInProgress === null) return; + var interruptedWork; + + if (workInProgressSuspendedReason === NotSuspended) { + // Normal case. Work-in-progress hasn't started yet. Unwind all + // its parents. + interruptedWork = workInProgress.return; + } else { + // Work-in-progress is in suspended state. Reset the work loop and unwind + // both the suspended fiber and all its parents. + resetSuspendedWorkLoopOnUnwind(); + interruptedWork = workInProgress; + } + + while (interruptedWork !== null) { + var current = interruptedWork.alternate; + unwindInterruptedWork(current, interruptedWork); + interruptedWork = interruptedWork.return; + } + + workInProgress = null; +} + function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = NoLanes; @@ -24127,27 +24170,7 @@ function prepareFreshStack(root, lanes) { cancelTimeout(timeoutHandle); } - if (workInProgress !== null) { - var interruptedWork; - - if (workInProgressSuspendedReason === NotSuspended) { - // Normal case. Work-in-progress hasn't started yet. Unwind all - // its parents. - interruptedWork = workInProgress.return; - } else { - // Work-in-progress is in suspended state. Reset the work loop and unwind - // both the suspended fiber and all its parents. - resetSuspendedWorkLoopOnUnwind(); - interruptedWork = workInProgress; - } - - while (interruptedWork !== null) { - var current = interruptedWork.alternate; - unwindInterruptedWork(current, interruptedWork); - interruptedWork = interruptedWork.return; - } - } - + resetWorkInProgressStack(); workInProgressRoot = root; var rootWorkInProgress = createWorkInProgress(root.current, null); workInProgress = rootWorkInProgress; @@ -24206,6 +24229,17 @@ function handleThrow(root, thrownValue) { workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves() ? SuspendedOnData : SuspendedOnImmediate; + } else if (thrownValue === SelectiveHydrationException) { + // An update flowed into a dehydrated boundary. Before we can apply the + // update, we need to finish hydrating. Interrupt the work-in-progress + // render so we can restart at the hydration lane. + // + // The ideal implementation would be able to switch contexts without + // unwinding the current stack. + // + // We could name this something more general but as of now it's the only + // case where we think this should happen. + workInProgressSuspendedReason = SuspendedOnHydration; } else { // This is a regular error. var isWakeable = @@ -24431,7 +24465,7 @@ function renderRootSync(root, lanes) { markRenderStarted(lanes); } - do { + outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && @@ -24447,9 +24481,25 @@ function renderRootSync(root, lanes) { // function and fork the behavior some other way. var unitOfWork = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = NotSuspended; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(unitOfWork, thrownValue); // Continue with the normal work loop. + + switch (workInProgressSuspendedReason) { + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + + default: { + // Continue with the normal work loop. + workInProgressSuspendedReason = NotSuspended; + workInProgressThrownValue = null; + unwindSuspendedUnitOfWork(unitOfWork, thrownValue); + break; + } + } } workLoopSync(); @@ -24614,6 +24664,15 @@ function renderRootConcurrent(root, lanes) { break; } + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + default: { throw new Error( "Unexpected SuspendedReason. This is a bug in React." @@ -25983,6 +26042,7 @@ if (replayFailedUnitOfWorkWithInvokeGuardedCallback) { if ( didSuspendOrErrorWhileHydratingDEV() || originalError === SuspenseException || + originalError === SelectiveHydrationException || (originalError !== null && typeof originalError === "object" && typeof originalError.then === "function") diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index 0da763a7f7149..468df0ee6f252 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-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-modern-7efa9e597-20221215"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -12444,7 +12444,14 @@ function getMarkerInstances() { return null; } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows +// into a dehydrated boundary. + +var SelectiveHydrationException = new Error( + "This is not a real error. It's an implementation detail of React's " + + "selective hydration feature. If this leaks into userspace, it's a bug in " + + "React. Please file an issue." +); var didReceiveUpdate = false; var didWarnAboutBadClass; var didWarnAboutModulePatternComponent; @@ -14623,18 +14630,29 @@ function updateDehydratedSuspenseComponent( current, attemptHydrationAtLane, eventTime - ); - } - } // If we have scheduled higher pri work above, this will just abort the render - // since we now have higher priority work. We'll try to infinitely suspend until - // we yield. TODO: We could probably just force yielding earlier instead. + ); // Throw a special object that signals to the work loop that it should + // interrupt the current render. + // + // Because we're inside a React-only execution stack, we don't + // strictly need to throw here — we could instead modify some internal + // work loop state. But using an exception means we don't need to + // check for this case on every iteration of the work loop. So doing + // it this way moves the check out of the fast path. - renderDidSuspendDelayIfPossible(); // If we rendered synchronously, we won't yield so have to render something. - // This will cause us to delete any existing content. + throw SelectiveHydrationException; + } + } // If we did not selectively hydrate, we'll continue rendering without + // hydrating. Mark this tree as suspended to prevent it from committing + // outside a transition. + // + // This path should only happen if the hydration lane already suspended. + // Currently, it also happens during sync updates because there is no + // hydration lane for sync updates. // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. + renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( current, workInProgress, @@ -22609,7 +22627,8 @@ var SuspendedOnError = 1; var SuspendedOnData = 2; var SuspendedOnImmediate = 3; var SuspendedOnDeprecatedThrowPromise = 4; -var SuspendedAndReadyToUnwind = 5; // When this is true, the work-in-progress fiber just suspended (or errored) and +var SuspendedAndReadyToUnwind = 5; +var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and // we've yet to unwind the stack. In some cases, we may yield to the main thread // after this happens. If the fiber is pinged before we resume, we can retry // immediately instead of unwinding the stack. @@ -23803,6 +23822,30 @@ function getRenderLanes() { return renderLanes$1; } +function resetWorkInProgressStack() { + if (workInProgress === null) return; + var interruptedWork; + + if (workInProgressSuspendedReason === NotSuspended) { + // Normal case. Work-in-progress hasn't started yet. Unwind all + // its parents. + interruptedWork = workInProgress.return; + } else { + // Work-in-progress is in suspended state. Reset the work loop and unwind + // both the suspended fiber and all its parents. + resetSuspendedWorkLoopOnUnwind(); + interruptedWork = workInProgress; + } + + while (interruptedWork !== null) { + var current = interruptedWork.alternate; + unwindInterruptedWork(current, interruptedWork); + interruptedWork = interruptedWork.return; + } + + workInProgress = null; +} + function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = NoLanes; @@ -23816,27 +23859,7 @@ function prepareFreshStack(root, lanes) { cancelTimeout(timeoutHandle); } - if (workInProgress !== null) { - var interruptedWork; - - if (workInProgressSuspendedReason === NotSuspended) { - // Normal case. Work-in-progress hasn't started yet. Unwind all - // its parents. - interruptedWork = workInProgress.return; - } else { - // Work-in-progress is in suspended state. Reset the work loop and unwind - // both the suspended fiber and all its parents. - resetSuspendedWorkLoopOnUnwind(); - interruptedWork = workInProgress; - } - - while (interruptedWork !== null) { - var current = interruptedWork.alternate; - unwindInterruptedWork(current, interruptedWork); - interruptedWork = interruptedWork.return; - } - } - + resetWorkInProgressStack(); workInProgressRoot = root; var rootWorkInProgress = createWorkInProgress(root.current, null); workInProgress = rootWorkInProgress; @@ -23895,6 +23918,17 @@ function handleThrow(root, thrownValue) { workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves() ? SuspendedOnData : SuspendedOnImmediate; + } else if (thrownValue === SelectiveHydrationException) { + // An update flowed into a dehydrated boundary. Before we can apply the + // update, we need to finish hydrating. Interrupt the work-in-progress + // render so we can restart at the hydration lane. + // + // The ideal implementation would be able to switch contexts without + // unwinding the current stack. + // + // We could name this something more general but as of now it's the only + // case where we think this should happen. + workInProgressSuspendedReason = SuspendedOnHydration; } else { // This is a regular error. var isWakeable = @@ -24120,7 +24154,7 @@ function renderRootSync(root, lanes) { markRenderStarted(lanes); } - do { + outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && @@ -24136,9 +24170,25 @@ function renderRootSync(root, lanes) { // function and fork the behavior some other way. var unitOfWork = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = NotSuspended; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(unitOfWork, thrownValue); // Continue with the normal work loop. + + switch (workInProgressSuspendedReason) { + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + + default: { + // Continue with the normal work loop. + workInProgressSuspendedReason = NotSuspended; + workInProgressThrownValue = null; + unwindSuspendedUnitOfWork(unitOfWork, thrownValue); + break; + } + } } workLoopSync(); @@ -24303,6 +24353,15 @@ function renderRootConcurrent(root, lanes) { break; } + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + default: { throw new Error( "Unexpected SuspendedReason. This is a bug in React." @@ -25672,6 +25731,7 @@ if (replayFailedUnitOfWorkWithInvokeGuardedCallback) { if ( didSuspendOrErrorWhileHydratingDEV() || originalError === SuspenseException || + originalError === SelectiveHydrationException || (originalError !== null && typeof originalError === "object" && typeof originalError.then === "function") diff --git a/compiled/facebook-www/ReactART-prod.classic.js b/compiled/facebook-www/ReactART-prod.classic.js index b3d2101e56109..a37a8c94511ae 100644 --- a/compiled/facebook-www/ReactART-prod.classic.js +++ b/compiled/facebook-www/ReactART-prod.classic.js @@ -3585,6 +3585,7 @@ function pushMarkerInstance(workInProgress, markerInstance) { )); } var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + SelectiveHydrationException = Error(formatProdErrorMessage(461)), didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -4490,11 +4491,11 @@ function updateDehydratedSuspenseComponent( 0 !== (didSuspend & (nextProps.suspendedLanes | renderLanes)) ? 0 : didSuspend; - 0 !== didSuspend && - didSuspend !== suspenseState.retryLane && - ((suspenseState.retryLane = didSuspend), + if (0 !== didSuspend && didSuspend !== suspenseState.retryLane) + throw ((suspenseState.retryLane = didSuspend), enqueueConcurrentRenderForLane(current, didSuspend), - scheduleUpdateOnFiber(nextProps, current, didSuspend, -1)); + scheduleUpdateOnFiber(nextProps, current, didSuspend, -1), + SelectiveHydrationException); } renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( @@ -8128,24 +8129,27 @@ function performSyncWorkOnRoot(root) { ensureRootIsScheduled(root, now()); return null; } +function resetWorkInProgressStack() { + if (null !== workInProgress) { + if (0 === workInProgressSuspendedReason) + var interruptedWork = workInProgress.return; + else + resetContextDependencies(), + resetHooksOnUnwind(), + (interruptedWork = workInProgress); + for (; null !== interruptedWork; ) + unwindInterruptedWork(interruptedWork.alternate, interruptedWork), + (interruptedWork = interruptedWork.return); + workInProgress = null; + } +} function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = 0; var timeoutHandle = root.timeoutHandle; -1 !== timeoutHandle && ((root.timeoutHandle = -1), cancelTimeout(timeoutHandle)); - if (null !== workInProgress) - for ( - 0 === workInProgressSuspendedReason - ? (timeoutHandle = workInProgress.return) - : (resetContextDependencies(), - resetHooksOnUnwind(), - (timeoutHandle = workInProgress)); - null !== timeoutHandle; - - ) - unwindInterruptedWork(timeoutHandle.alternate, timeoutHandle), - (timeoutHandle = timeoutHandle.return); + resetWorkInProgressStack(); workInProgressRoot = root; workInProgress = root = createWorkInProgress(root.current, null); workInProgressRootRenderLanes = renderLanes$1 = lanes; @@ -8172,9 +8176,11 @@ function handleThrow(root, thrownValue) { : 3; } else workInProgressSuspendedReason = - null !== thrownValue && - "object" === typeof thrownValue && - "function" === typeof thrownValue.then + thrownValue === SelectiveHydrationException + ? 6 + : null !== thrownValue && + "object" === typeof thrownValue && + "function" === typeof thrownValue.then ? 4 : 1; workInProgressThrownValue = thrownValue; @@ -8234,14 +8240,21 @@ function renderRootSync(root, lanes) { if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) (workInProgressTransitions = getTransitionsForLanes(root, lanes)), prepareFreshStack(root, lanes); - do + a: do try { if (0 !== workInProgressSuspendedReason && null !== workInProgress) { lanes = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = 0; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(lanes, thrownValue); + switch (workInProgressSuspendedReason) { + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; + default: + (workInProgressSuspendedReason = 0), + (workInProgressThrownValue = null), + unwindSuspendedUnitOfWork(lanes, thrownValue); + } } workLoopSync(); break; @@ -8311,6 +8324,10 @@ function renderRootConcurrent(root, lanes) { workInProgressThrownValue = null; unwindSuspendedUnitOfWork(lanes, thrownValue); break; + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; default: throw Error(formatProdErrorMessage(462)); } @@ -9807,7 +9824,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "18.3.0-www-classic-84a0a171e-20221214", + version: "18.3.0-www-classic-7efa9e597-20221215", rendererPackageName: "react-art" }; var internals$jscomp$inline_1338 = { @@ -9838,7 +9855,7 @@ var internals$jscomp$inline_1338 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-84a0a171e-20221214" + reconcilerVersion: "18.3.0-next-7efa9e597-20221215" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1339 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js index 02b3fbbc06edd..f7292e091ab10 100644 --- a/compiled/facebook-www/ReactART-prod.modern.js +++ b/compiled/facebook-www/ReactART-prod.modern.js @@ -3378,6 +3378,7 @@ function pushMarkerInstance(workInProgress, markerInstance) { )); } var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + SelectiveHydrationException = Error(formatProdErrorMessage(461)), didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -4247,11 +4248,11 @@ function updateDehydratedSuspenseComponent( 0 !== (didSuspend & (nextProps.suspendedLanes | renderLanes)) ? 0 : didSuspend; - 0 !== didSuspend && - didSuspend !== suspenseState.retryLane && - ((suspenseState.retryLane = didSuspend), + if (0 !== didSuspend && didSuspend !== suspenseState.retryLane) + throw ((suspenseState.retryLane = didSuspend), enqueueConcurrentRenderForLane(current, didSuspend), - scheduleUpdateOnFiber(nextProps, current, didSuspend, -1)); + scheduleUpdateOnFiber(nextProps, current, didSuspend, -1), + SelectiveHydrationException); } renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( @@ -7862,24 +7863,27 @@ function performSyncWorkOnRoot(root) { ensureRootIsScheduled(root, now()); return null; } +function resetWorkInProgressStack() { + if (null !== workInProgress) { + if (0 === workInProgressSuspendedReason) + var interruptedWork = workInProgress.return; + else + resetContextDependencies(), + resetHooksOnUnwind(), + (interruptedWork = workInProgress); + for (; null !== interruptedWork; ) + unwindInterruptedWork(interruptedWork.alternate, interruptedWork), + (interruptedWork = interruptedWork.return); + workInProgress = null; + } +} function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = 0; var timeoutHandle = root.timeoutHandle; -1 !== timeoutHandle && ((root.timeoutHandle = -1), cancelTimeout(timeoutHandle)); - if (null !== workInProgress) - for ( - 0 === workInProgressSuspendedReason - ? (timeoutHandle = workInProgress.return) - : (resetContextDependencies(), - resetHooksOnUnwind(), - (timeoutHandle = workInProgress)); - null !== timeoutHandle; - - ) - unwindInterruptedWork(timeoutHandle.alternate, timeoutHandle), - (timeoutHandle = timeoutHandle.return); + resetWorkInProgressStack(); workInProgressRoot = root; workInProgress = root = createWorkInProgress(root.current, null); workInProgressRootRenderLanes = renderLanes$1 = lanes; @@ -7906,9 +7910,11 @@ function handleThrow(root, thrownValue) { : 3; } else workInProgressSuspendedReason = - null !== thrownValue && - "object" === typeof thrownValue && - "function" === typeof thrownValue.then + thrownValue === SelectiveHydrationException + ? 6 + : null !== thrownValue && + "object" === typeof thrownValue && + "function" === typeof thrownValue.then ? 4 : 1; workInProgressThrownValue = thrownValue; @@ -7968,14 +7974,21 @@ function renderRootSync(root, lanes) { if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) (workInProgressTransitions = getTransitionsForLanes(root, lanes)), prepareFreshStack(root, lanes); - do + a: do try { if (0 !== workInProgressSuspendedReason && null !== workInProgress) { lanes = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = 0; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(lanes, thrownValue); + switch (workInProgressSuspendedReason) { + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; + default: + (workInProgressSuspendedReason = 0), + (workInProgressThrownValue = null), + unwindSuspendedUnitOfWork(lanes, thrownValue); + } } workLoopSync(); break; @@ -8045,6 +8058,10 @@ function renderRootConcurrent(root, lanes) { workInProgressThrownValue = null; unwindSuspendedUnitOfWork(lanes, thrownValue); break; + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; default: throw Error(formatProdErrorMessage(462)); } @@ -9474,7 +9491,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "18.3.0-www-modern-84a0a171e-20221214", + version: "18.3.0-www-modern-7efa9e597-20221215", rendererPackageName: "react-art" }; var internals$jscomp$inline_1329 = { @@ -9505,7 +9522,7 @@ var internals$jscomp$inline_1329 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-84a0a171e-20221214" + reconcilerVersion: "18.3.0-next-7efa9e597-20221215" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1330 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactDOM-dev.classic.js b/compiled/facebook-www/ReactDOM-dev.classic.js index 2a2a8df0b9efc..013aa94bf408e 100644 --- a/compiled/facebook-www/ReactDOM-dev.classic.js +++ b/compiled/facebook-www/ReactDOM-dev.classic.js @@ -27059,7 +27059,14 @@ function getMarkerInstances() { return null; } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows +// into a dehydrated boundary. + +var SelectiveHydrationException = new Error( + "This is not a real error. It's an implementation detail of React's " + + "selective hydration feature. If this leaks into userspace, it's a bug in " + + "React. Please file an issue." +); var didReceiveUpdate = false; var didWarnAboutBadClass; var didWarnAboutModulePatternComponent; @@ -29511,18 +29518,29 @@ function updateDehydratedSuspenseComponent( current, attemptHydrationAtLane, eventTime - ); - } - } // If we have scheduled higher pri work above, this will just abort the render - // since we now have higher priority work. We'll try to infinitely suspend until - // we yield. TODO: We could probably just force yielding earlier instead. + ); // Throw a special object that signals to the work loop that it should + // interrupt the current render. + // + // Because we're inside a React-only execution stack, we don't + // strictly need to throw here — we could instead modify some internal + // work loop state. But using an exception means we don't need to + // check for this case on every iteration of the work loop. So doing + // it this way moves the check out of the fast path. - renderDidSuspendDelayIfPossible(); // If we rendered synchronously, we won't yield so have to render something. - // This will cause us to delete any existing content. + throw SelectiveHydrationException; + } + } // If we did not selectively hydrate, we'll continue rendering without + // hydrating. Mark this tree as suspended to prevent it from committing + // outside a transition. + // + // This path should only happen if the hydration lane already suspended. + // Currently, it also happens during sync updates because there is no + // hydration lane for sync updates. // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. + renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( current, workInProgress, @@ -37820,7 +37838,8 @@ var SuspendedOnError = 1; var SuspendedOnData = 2; var SuspendedOnImmediate = 3; var SuspendedOnDeprecatedThrowPromise = 4; -var SuspendedAndReadyToUnwind = 5; // When this is true, the work-in-progress fiber just suspended (or errored) and +var SuspendedAndReadyToUnwind = 5; +var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and // we've yet to unwind the stack. In some cases, we may yield to the main thread // after this happens. If the fiber is pinged before we resume, we can retry // immediately instead of unwinding the stack. @@ -39124,6 +39143,30 @@ function getRenderLanes() { return renderLanes$1; } +function resetWorkInProgressStack() { + if (workInProgress === null) return; + var interruptedWork; + + if (workInProgressSuspendedReason === NotSuspended) { + // Normal case. Work-in-progress hasn't started yet. Unwind all + // its parents. + interruptedWork = workInProgress.return; + } else { + // Work-in-progress is in suspended state. Reset the work loop and unwind + // both the suspended fiber and all its parents. + resetSuspendedWorkLoopOnUnwind(); + interruptedWork = workInProgress; + } + + while (interruptedWork !== null) { + var current = interruptedWork.alternate; + unwindInterruptedWork(current, interruptedWork); + interruptedWork = interruptedWork.return; + } + + workInProgress = null; +} + function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = NoLanes; @@ -39137,27 +39180,7 @@ function prepareFreshStack(root, lanes) { cancelTimeout(timeoutHandle); } - if (workInProgress !== null) { - var interruptedWork; - - if (workInProgressSuspendedReason === NotSuspended) { - // Normal case. Work-in-progress hasn't started yet. Unwind all - // its parents. - interruptedWork = workInProgress.return; - } else { - // Work-in-progress is in suspended state. Reset the work loop and unwind - // both the suspended fiber and all its parents. - resetSuspendedWorkLoopOnUnwind(); - interruptedWork = workInProgress; - } - - while (interruptedWork !== null) { - var current = interruptedWork.alternate; - unwindInterruptedWork(current, interruptedWork); - interruptedWork = interruptedWork.return; - } - } - + resetWorkInProgressStack(); workInProgressRoot = root; var rootWorkInProgress = createWorkInProgress(root.current, null); workInProgress = rootWorkInProgress; @@ -39216,6 +39239,17 @@ function handleThrow(root, thrownValue) { workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves() ? SuspendedOnData : SuspendedOnImmediate; + } else if (thrownValue === SelectiveHydrationException) { + // An update flowed into a dehydrated boundary. Before we can apply the + // update, we need to finish hydrating. Interrupt the work-in-progress + // render so we can restart at the hydration lane. + // + // The ideal implementation would be able to switch contexts without + // unwinding the current stack. + // + // We could name this something more general but as of now it's the only + // case where we think this should happen. + workInProgressSuspendedReason = SuspendedOnHydration; } else { // This is a regular error. var isWakeable = @@ -39443,7 +39477,7 @@ function renderRootSync(root, lanes) { markRenderStarted(lanes); } - do { + outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && @@ -39459,9 +39493,25 @@ function renderRootSync(root, lanes) { // function and fork the behavior some other way. var unitOfWork = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = NotSuspended; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(unitOfWork, thrownValue); // Continue with the normal work loop. + + switch (workInProgressSuspendedReason) { + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + + default: { + // Continue with the normal work loop. + workInProgressSuspendedReason = NotSuspended; + workInProgressThrownValue = null; + unwindSuspendedUnitOfWork(unitOfWork, thrownValue); + break; + } + } } workLoopSync(); @@ -39626,6 +39676,15 @@ function renderRootConcurrent(root, lanes) { break; } + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + default: { throw new Error( "Unexpected SuspendedReason. This is a bug in React." @@ -41024,6 +41083,7 @@ if (replayFailedUnitOfWorkWithInvokeGuardedCallback) { if ( didSuspendOrErrorWhileHydratingDEV() || originalError === SuspenseException || + originalError === SelectiveHydrationException || (originalError !== null && typeof originalError === "object" && typeof originalError.then === "function") @@ -42571,7 +42631,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-classic-7efa9e597-20221215"; function createPortal( children, diff --git a/compiled/facebook-www/ReactDOM-dev.modern.js b/compiled/facebook-www/ReactDOM-dev.modern.js index bb3228355a755..376e306544ca8 100644 --- a/compiled/facebook-www/ReactDOM-dev.modern.js +++ b/compiled/facebook-www/ReactDOM-dev.modern.js @@ -26822,7 +26822,14 @@ function getMarkerInstances() { return null; } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows +// into a dehydrated boundary. + +var SelectiveHydrationException = new Error( + "This is not a real error. It's an implementation detail of React's " + + "selective hydration feature. If this leaks into userspace, it's a bug in " + + "React. Please file an issue." +); var didReceiveUpdate = false; var didWarnAboutBadClass; var didWarnAboutModulePatternComponent; @@ -29258,18 +29265,29 @@ function updateDehydratedSuspenseComponent( current, attemptHydrationAtLane, eventTime - ); - } - } // If we have scheduled higher pri work above, this will just abort the render - // since we now have higher priority work. We'll try to infinitely suspend until - // we yield. TODO: We could probably just force yielding earlier instead. + ); // Throw a special object that signals to the work loop that it should + // interrupt the current render. + // + // Because we're inside a React-only execution stack, we don't + // strictly need to throw here — we could instead modify some internal + // work loop state. But using an exception means we don't need to + // check for this case on every iteration of the work loop. So doing + // it this way moves the check out of the fast path. - renderDidSuspendDelayIfPossible(); // If we rendered synchronously, we won't yield so have to render something. - // This will cause us to delete any existing content. + throw SelectiveHydrationException; + } + } // If we did not selectively hydrate, we'll continue rendering without + // hydrating. Mark this tree as suspended to prevent it from committing + // outside a transition. + // + // This path should only happen if the hydration lane already suspended. + // Currently, it also happens during sync updates because there is no + // hydration lane for sync updates. // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. + renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( current, workInProgress, @@ -37544,7 +37562,8 @@ var SuspendedOnError = 1; var SuspendedOnData = 2; var SuspendedOnImmediate = 3; var SuspendedOnDeprecatedThrowPromise = 4; -var SuspendedAndReadyToUnwind = 5; // When this is true, the work-in-progress fiber just suspended (or errored) and +var SuspendedAndReadyToUnwind = 5; +var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and // we've yet to unwind the stack. In some cases, we may yield to the main thread // after this happens. If the fiber is pinged before we resume, we can retry // immediately instead of unwinding the stack. @@ -38848,6 +38867,30 @@ function getRenderLanes() { return renderLanes$1; } +function resetWorkInProgressStack() { + if (workInProgress === null) return; + var interruptedWork; + + if (workInProgressSuspendedReason === NotSuspended) { + // Normal case. Work-in-progress hasn't started yet. Unwind all + // its parents. + interruptedWork = workInProgress.return; + } else { + // Work-in-progress is in suspended state. Reset the work loop and unwind + // both the suspended fiber and all its parents. + resetSuspendedWorkLoopOnUnwind(); + interruptedWork = workInProgress; + } + + while (interruptedWork !== null) { + var current = interruptedWork.alternate; + unwindInterruptedWork(current, interruptedWork); + interruptedWork = interruptedWork.return; + } + + workInProgress = null; +} + function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = NoLanes; @@ -38861,27 +38904,7 @@ function prepareFreshStack(root, lanes) { cancelTimeout(timeoutHandle); } - if (workInProgress !== null) { - var interruptedWork; - - if (workInProgressSuspendedReason === NotSuspended) { - // Normal case. Work-in-progress hasn't started yet. Unwind all - // its parents. - interruptedWork = workInProgress.return; - } else { - // Work-in-progress is in suspended state. Reset the work loop and unwind - // both the suspended fiber and all its parents. - resetSuspendedWorkLoopOnUnwind(); - interruptedWork = workInProgress; - } - - while (interruptedWork !== null) { - var current = interruptedWork.alternate; - unwindInterruptedWork(current, interruptedWork); - interruptedWork = interruptedWork.return; - } - } - + resetWorkInProgressStack(); workInProgressRoot = root; var rootWorkInProgress = createWorkInProgress(root.current, null); workInProgress = rootWorkInProgress; @@ -38940,6 +38963,17 @@ function handleThrow(root, thrownValue) { workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves() ? SuspendedOnData : SuspendedOnImmediate; + } else if (thrownValue === SelectiveHydrationException) { + // An update flowed into a dehydrated boundary. Before we can apply the + // update, we need to finish hydrating. Interrupt the work-in-progress + // render so we can restart at the hydration lane. + // + // The ideal implementation would be able to switch contexts without + // unwinding the current stack. + // + // We could name this something more general but as of now it's the only + // case where we think this should happen. + workInProgressSuspendedReason = SuspendedOnHydration; } else { // This is a regular error. var isWakeable = @@ -39167,7 +39201,7 @@ function renderRootSync(root, lanes) { markRenderStarted(lanes); } - do { + outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && @@ -39183,9 +39217,25 @@ function renderRootSync(root, lanes) { // function and fork the behavior some other way. var unitOfWork = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = NotSuspended; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(unitOfWork, thrownValue); // Continue with the normal work loop. + + switch (workInProgressSuspendedReason) { + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + + default: { + // Continue with the normal work loop. + workInProgressSuspendedReason = NotSuspended; + workInProgressThrownValue = null; + unwindSuspendedUnitOfWork(unitOfWork, thrownValue); + break; + } + } } workLoopSync(); @@ -39350,6 +39400,15 @@ function renderRootConcurrent(root, lanes) { break; } + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + default: { throw new Error( "Unexpected SuspendedReason. This is a bug in React." @@ -40748,6 +40807,7 @@ if (replayFailedUnitOfWorkWithInvokeGuardedCallback) { if ( didSuspendOrErrorWhileHydratingDEV() || originalError === SuspenseException || + originalError === SelectiveHydrationException || (originalError !== null && typeof originalError === "object" && typeof originalError.then === "function") @@ -42295,7 +42355,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-modern-7efa9e597-20221215"; function createPortal( children, diff --git a/compiled/facebook-www/ReactDOM-prod.classic.js b/compiled/facebook-www/ReactDOM-prod.classic.js index df545c65a3ebc..1c023f04e5058 100644 --- a/compiled/facebook-www/ReactDOM-prod.classic.js +++ b/compiled/facebook-www/ReactDOM-prod.classic.js @@ -8261,6 +8261,7 @@ function pushMarkerInstance(workInProgress, markerInstance) { )); } var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + SelectiveHydrationException = Error(formatProdErrorMessage(461)), didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -9202,11 +9203,14 @@ function updateDehydratedSuspenseComponent( 0 !== (suspenseInstance & (nextProps.suspendedLanes | renderLanes)) ? 0 : suspenseInstance; - 0 !== suspenseInstance && - suspenseInstance !== suspenseState.retryLane && - ((suspenseState.retryLane = suspenseInstance), + if ( + 0 !== suspenseInstance && + suspenseInstance !== suspenseState.retryLane + ) + throw ((suspenseState.retryLane = suspenseInstance), enqueueConcurrentRenderForLane(current, suspenseInstance), - scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1)); + scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1), + SelectiveHydrationException); } renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( @@ -13451,24 +13455,27 @@ function flushSync(fn) { 0 === (executionContext & 6) && flushSyncCallbacks(); } } +function resetWorkInProgressStack() { + if (null !== workInProgress) { + if (0 === workInProgressSuspendedReason) + var interruptedWork = workInProgress.return; + else + resetContextDependencies(), + resetHooksOnUnwind(), + (interruptedWork = workInProgress); + for (; null !== interruptedWork; ) + unwindInterruptedWork(interruptedWork.alternate, interruptedWork), + (interruptedWork = interruptedWork.return); + workInProgress = null; + } +} function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = 0; var timeoutHandle = root.timeoutHandle; -1 !== timeoutHandle && ((root.timeoutHandle = -1), cancelTimeout(timeoutHandle)); - if (null !== workInProgress) - for ( - 0 === workInProgressSuspendedReason - ? (timeoutHandle = workInProgress.return) - : (resetContextDependencies(), - resetHooksOnUnwind(), - (timeoutHandle = workInProgress)); - null !== timeoutHandle; - - ) - unwindInterruptedWork(timeoutHandle.alternate, timeoutHandle), - (timeoutHandle = timeoutHandle.return); + resetWorkInProgressStack(); workInProgressRoot = root; workInProgress = root = createWorkInProgress(root.current, null); workInProgressRootRenderLanes = renderLanes$1 = lanes; @@ -13495,9 +13502,11 @@ function handleThrow(root, thrownValue) { : 3; } else workInProgressSuspendedReason = - null !== thrownValue && - "object" === typeof thrownValue && - "function" === typeof thrownValue.then + thrownValue === SelectiveHydrationException + ? 6 + : null !== thrownValue && + "object" === typeof thrownValue && + "function" === typeof thrownValue.then ? 4 : 1; workInProgressThrownValue = thrownValue; @@ -13561,14 +13570,21 @@ function renderRootSync(root, lanes) { if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) (workInProgressTransitions = getTransitionsForLanes(root, lanes)), prepareFreshStack(root, lanes); - do + a: do try { if (0 !== workInProgressSuspendedReason && null !== workInProgress) { lanes = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = 0; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(lanes, thrownValue); + switch (workInProgressSuspendedReason) { + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; + default: + (workInProgressSuspendedReason = 0), + (workInProgressThrownValue = null), + unwindSuspendedUnitOfWork(lanes, thrownValue); + } } workLoopSync(); break; @@ -13640,6 +13656,10 @@ function renderRootConcurrent(root, lanes) { workInProgressThrownValue = null; unwindSuspendedUnitOfWork(lanes, thrownValue); break; + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; default: throw Error(formatProdErrorMessage(462)); } @@ -15557,7 +15577,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1770 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-classic-84a0a171e-20221214", + version: "18.3.0-www-classic-7efa9e597-20221215", rendererPackageName: "react-dom" }; var internals$jscomp$inline_2157 = { @@ -15587,7 +15607,7 @@ var internals$jscomp$inline_2157 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-84a0a171e-20221214" + reconcilerVersion: "18.3.0-next-7efa9e597-20221215" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2158 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -15829,4 +15849,4 @@ exports.unstable_renderSubtreeIntoContainer = function( ); }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-next-84a0a171e-20221214"; +exports.version = "18.3.0-next-7efa9e597-20221215"; diff --git a/compiled/facebook-www/ReactDOM-prod.modern.js b/compiled/facebook-www/ReactDOM-prod.modern.js index 00a63279d6956..c6b419b4364e9 100644 --- a/compiled/facebook-www/ReactDOM-prod.modern.js +++ b/compiled/facebook-www/ReactDOM-prod.modern.js @@ -8098,6 +8098,7 @@ function pushMarkerInstance(workInProgress, markerInstance) { )); } var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + SelectiveHydrationException = Error(formatProdErrorMessage(461)), didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -9003,11 +9004,14 @@ function updateDehydratedSuspenseComponent( 0 !== (suspenseInstance & (nextProps.suspendedLanes | renderLanes)) ? 0 : suspenseInstance; - 0 !== suspenseInstance && - suspenseInstance !== suspenseState.retryLane && - ((suspenseState.retryLane = suspenseInstance), + if ( + 0 !== suspenseInstance && + suspenseInstance !== suspenseState.retryLane + ) + throw ((suspenseState.retryLane = suspenseInstance), enqueueConcurrentRenderForLane(current, suspenseInstance), - scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1)); + scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1), + SelectiveHydrationException); } renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( @@ -13229,24 +13233,27 @@ function flushSync(fn) { 0 === (executionContext & 6) && flushSyncCallbacks(); } } +function resetWorkInProgressStack() { + if (null !== workInProgress) { + if (0 === workInProgressSuspendedReason) + var interruptedWork = workInProgress.return; + else + resetContextDependencies(), + resetHooksOnUnwind(), + (interruptedWork = workInProgress); + for (; null !== interruptedWork; ) + unwindInterruptedWork(interruptedWork.alternate, interruptedWork), + (interruptedWork = interruptedWork.return); + workInProgress = null; + } +} function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = 0; var timeoutHandle = root.timeoutHandle; -1 !== timeoutHandle && ((root.timeoutHandle = -1), cancelTimeout(timeoutHandle)); - if (null !== workInProgress) - for ( - 0 === workInProgressSuspendedReason - ? (timeoutHandle = workInProgress.return) - : (resetContextDependencies(), - resetHooksOnUnwind(), - (timeoutHandle = workInProgress)); - null !== timeoutHandle; - - ) - unwindInterruptedWork(timeoutHandle.alternate, timeoutHandle), - (timeoutHandle = timeoutHandle.return); + resetWorkInProgressStack(); workInProgressRoot = root; workInProgress = root = createWorkInProgress(root.current, null); workInProgressRootRenderLanes = renderLanes$1 = lanes; @@ -13273,9 +13280,11 @@ function handleThrow(root, thrownValue) { : 3; } else workInProgressSuspendedReason = - null !== thrownValue && - "object" === typeof thrownValue && - "function" === typeof thrownValue.then + thrownValue === SelectiveHydrationException + ? 6 + : null !== thrownValue && + "object" === typeof thrownValue && + "function" === typeof thrownValue.then ? 4 : 1; workInProgressThrownValue = thrownValue; @@ -13339,14 +13348,21 @@ function renderRootSync(root, lanes) { if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) (workInProgressTransitions = getTransitionsForLanes(root, lanes)), prepareFreshStack(root, lanes); - do + a: do try { if (0 !== workInProgressSuspendedReason && null !== workInProgress) { lanes = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = 0; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(lanes, thrownValue); + switch (workInProgressSuspendedReason) { + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; + default: + (workInProgressSuspendedReason = 0), + (workInProgressThrownValue = null), + unwindSuspendedUnitOfWork(lanes, thrownValue); + } } workLoopSync(); break; @@ -13418,6 +13434,10 @@ function renderRootConcurrent(root, lanes) { workInProgressThrownValue = null; unwindSuspendedUnitOfWork(lanes, thrownValue); break; + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; default: throw Error(formatProdErrorMessage(462)); } @@ -15118,7 +15138,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1738 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-modern-84a0a171e-20221214", + version: "18.3.0-www-modern-7efa9e597-20221215", rendererPackageName: "react-dom" }; var internals$jscomp$inline_2132 = { @@ -15149,7 +15169,7 @@ var internals$jscomp$inline_2132 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-84a0a171e-20221214" + reconcilerVersion: "18.3.0-next-7efa9e597-20221215" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2133 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -15337,4 +15357,4 @@ exports.unstable_flushControlled = function(fn) { } }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-next-84a0a171e-20221214"; +exports.version = "18.3.0-next-7efa9e597-20221215"; diff --git a/compiled/facebook-www/ReactDOM-profiling.classic.js b/compiled/facebook-www/ReactDOM-profiling.classic.js index d23c4c3ae3277..9dbc8a97a07dd 100644 --- a/compiled/facebook-www/ReactDOM-profiling.classic.js +++ b/compiled/facebook-www/ReactDOM-profiling.classic.js @@ -8479,6 +8479,7 @@ function pushMarkerInstance(workInProgress, markerInstance) { )); } var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + SelectiveHydrationException = Error(formatProdErrorMessage(461)), didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -9443,11 +9444,14 @@ function updateDehydratedSuspenseComponent( 0 !== (suspenseInstance & (nextProps.suspendedLanes | renderLanes)) ? 0 : suspenseInstance; - 0 !== suspenseInstance && - suspenseInstance !== suspenseState.retryLane && - ((suspenseState.retryLane = suspenseInstance), + if ( + 0 !== suspenseInstance && + suspenseInstance !== suspenseState.retryLane + ) + throw ((suspenseState.retryLane = suspenseInstance), enqueueConcurrentRenderForLane(current, suspenseInstance), - scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1)); + scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1), + SelectiveHydrationException); } renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( @@ -14062,24 +14066,27 @@ function flushSync(fn) { 0 === (executionContext & 6) && flushSyncCallbacks(); } } +function resetWorkInProgressStack() { + if (null !== workInProgress) { + if (0 === workInProgressSuspendedReason) + var interruptedWork = workInProgress.return; + else + resetContextDependencies(), + resetHooksOnUnwind(), + (interruptedWork = workInProgress); + for (; null !== interruptedWork; ) + unwindInterruptedWork(interruptedWork.alternate, interruptedWork), + (interruptedWork = interruptedWork.return); + workInProgress = null; + } +} function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = 0; var timeoutHandle = root.timeoutHandle; -1 !== timeoutHandle && ((root.timeoutHandle = -1), cancelTimeout(timeoutHandle)); - if (null !== workInProgress) - for ( - 0 === workInProgressSuspendedReason - ? (timeoutHandle = workInProgress.return) - : (resetContextDependencies(), - resetHooksOnUnwind(), - (timeoutHandle = workInProgress)); - null !== timeoutHandle; - - ) - unwindInterruptedWork(timeoutHandle.alternate, timeoutHandle), - (timeoutHandle = timeoutHandle.return); + resetWorkInProgressStack(); workInProgressRoot = root; workInProgress = root = createWorkInProgress(root.current, null); workInProgressRootRenderLanes = renderLanes$1 = lanes; @@ -14105,9 +14112,11 @@ function handleThrow(root, thrownValue) { : 3; } else workInProgressSuspendedReason = - null !== thrownValue && - "object" === typeof thrownValue && - "function" === typeof thrownValue.then + thrownValue === SelectiveHydrationException + ? 6 + : null !== thrownValue && + "object" === typeof thrownValue && + "function" === typeof thrownValue.then ? 4 : 1; workInProgressThrownValue = thrownValue; @@ -14202,15 +14211,23 @@ function renderRootSync(root, lanes) { prepareFreshStack(root, lanes); } enableSchedulingProfiler && markRenderStarted(lanes); - do + a: do try { - 0 !== workInProgressSuspendedReason && - null !== workInProgress && - ((lanes = workInProgress), - (memoizedUpdaters = workInProgressThrownValue), - (workInProgressSuspendedReason = 0), - (workInProgressThrownValue = null), - unwindSuspendedUnitOfWork(lanes, memoizedUpdaters)); + if (0 !== workInProgressSuspendedReason && null !== workInProgress) + switch ( + ((lanes = workInProgress), + (memoizedUpdaters = workInProgressThrownValue), + workInProgressSuspendedReason) + ) { + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; + default: + (workInProgressSuspendedReason = 0), + (workInProgressThrownValue = null), + unwindSuspendedUnitOfWork(lanes, memoizedUpdaters); + } workLoopSync(); break; } catch (thrownValue$239) { @@ -14293,6 +14310,10 @@ function renderRootConcurrent(root, lanes) { workInProgressThrownValue = null; unwindSuspendedUnitOfWork(lanes, memoizedUpdaters); break; + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; default: throw Error(formatProdErrorMessage(462)); } @@ -16327,7 +16348,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1844 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-classic-84a0a171e-20221214", + version: "18.3.0-www-classic-7efa9e597-20221215", rendererPackageName: "react-dom" }; (function(internals) { @@ -16371,7 +16392,7 @@ var devToolsConfig$jscomp$inline_1844 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-84a0a171e-20221214" + reconcilerVersion: "18.3.0-next-7efa9e597-20221215" }); assign(Internals, { ReactBrowserEventEmitter: { @@ -16600,7 +16621,7 @@ exports.unstable_renderSubtreeIntoContainer = function( ); }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-next-84a0a171e-20221214"; +exports.version = "18.3.0-next-7efa9e597-20221215"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( diff --git a/compiled/facebook-www/ReactDOM-profiling.modern.js b/compiled/facebook-www/ReactDOM-profiling.modern.js index 5abe481b5ba44..9f09389ef6cfe 100644 --- a/compiled/facebook-www/ReactDOM-profiling.modern.js +++ b/compiled/facebook-www/ReactDOM-profiling.modern.js @@ -8312,6 +8312,7 @@ function pushMarkerInstance(workInProgress, markerInstance) { )); } var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + SelectiveHydrationException = Error(formatProdErrorMessage(461)), didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -9234,11 +9235,14 @@ function updateDehydratedSuspenseComponent( 0 !== (suspenseInstance & (nextProps.suspendedLanes | renderLanes)) ? 0 : suspenseInstance; - 0 !== suspenseInstance && - suspenseInstance !== suspenseState.retryLane && - ((suspenseState.retryLane = suspenseInstance), + if ( + 0 !== suspenseInstance && + suspenseInstance !== suspenseState.retryLane + ) + throw ((suspenseState.retryLane = suspenseInstance), enqueueConcurrentRenderForLane(current, suspenseInstance), - scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1)); + scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1), + SelectiveHydrationException); } renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( @@ -13830,24 +13834,27 @@ function flushSync(fn) { 0 === (executionContext & 6) && flushSyncCallbacks(); } } +function resetWorkInProgressStack() { + if (null !== workInProgress) { + if (0 === workInProgressSuspendedReason) + var interruptedWork = workInProgress.return; + else + resetContextDependencies(), + resetHooksOnUnwind(), + (interruptedWork = workInProgress); + for (; null !== interruptedWork; ) + unwindInterruptedWork(interruptedWork.alternate, interruptedWork), + (interruptedWork = interruptedWork.return); + workInProgress = null; + } +} function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = 0; var timeoutHandle = root.timeoutHandle; -1 !== timeoutHandle && ((root.timeoutHandle = -1), cancelTimeout(timeoutHandle)); - if (null !== workInProgress) - for ( - 0 === workInProgressSuspendedReason - ? (timeoutHandle = workInProgress.return) - : (resetContextDependencies(), - resetHooksOnUnwind(), - (timeoutHandle = workInProgress)); - null !== timeoutHandle; - - ) - unwindInterruptedWork(timeoutHandle.alternate, timeoutHandle), - (timeoutHandle = timeoutHandle.return); + resetWorkInProgressStack(); workInProgressRoot = root; workInProgress = root = createWorkInProgress(root.current, null); workInProgressRootRenderLanes = renderLanes$1 = lanes; @@ -13873,9 +13880,11 @@ function handleThrow(root, thrownValue) { : 3; } else workInProgressSuspendedReason = - null !== thrownValue && - "object" === typeof thrownValue && - "function" === typeof thrownValue.then + thrownValue === SelectiveHydrationException + ? 6 + : null !== thrownValue && + "object" === typeof thrownValue && + "function" === typeof thrownValue.then ? 4 : 1; workInProgressThrownValue = thrownValue; @@ -13970,15 +13979,23 @@ function renderRootSync(root, lanes) { prepareFreshStack(root, lanes); } enableSchedulingProfiler && markRenderStarted(lanes); - do + a: do try { - 0 !== workInProgressSuspendedReason && - null !== workInProgress && - ((lanes = workInProgress), - (memoizedUpdaters = workInProgressThrownValue), - (workInProgressSuspendedReason = 0), - (workInProgressThrownValue = null), - unwindSuspendedUnitOfWork(lanes, memoizedUpdaters)); + if (0 !== workInProgressSuspendedReason && null !== workInProgress) + switch ( + ((lanes = workInProgress), + (memoizedUpdaters = workInProgressThrownValue), + workInProgressSuspendedReason) + ) { + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; + default: + (workInProgressSuspendedReason = 0), + (workInProgressThrownValue = null), + unwindSuspendedUnitOfWork(lanes, memoizedUpdaters); + } workLoopSync(); break; } catch (thrownValue$240) { @@ -14061,6 +14078,10 @@ function renderRootConcurrent(root, lanes) { workInProgressThrownValue = null; unwindSuspendedUnitOfWork(lanes, memoizedUpdaters); break; + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; default: throw Error(formatProdErrorMessage(462)); } @@ -15878,7 +15899,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1812 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-modern-84a0a171e-20221214", + version: "18.3.0-www-modern-7efa9e597-20221215", rendererPackageName: "react-dom" }; (function(internals) { @@ -15923,7 +15944,7 @@ var devToolsConfig$jscomp$inline_1812 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-84a0a171e-20221214" + reconcilerVersion: "18.3.0-next-7efa9e597-20221215" }); exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals; exports.createPortal = function(children, container) { @@ -16098,7 +16119,7 @@ exports.unstable_flushControlled = function(fn) { } }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-next-84a0a171e-20221214"; +exports.version = "18.3.0-next-7efa9e597-20221215"; /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */ if ( diff --git a/compiled/facebook-www/ReactDOMServer-dev.classic.js b/compiled/facebook-www/ReactDOMServer-dev.classic.js index fe8df147eff01..6570e12661f6f 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.classic.js +++ b/compiled/facebook-www/ReactDOMServer-dev.classic.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-classic-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-classic-7efa9e597-20221215"; // This refers to a WWW module. var warningWWW = require("warning"); diff --git a/compiled/facebook-www/ReactDOMServer-dev.modern.js b/compiled/facebook-www/ReactDOMServer-dev.modern.js index 0656823881484..688ffb77ff6f1 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServer-dev.modern.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-modern-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-modern-7efa9e597-20221215"; // This refers to a WWW module. var warningWWW = require("warning"); diff --git a/compiled/facebook-www/ReactDOMServer-prod.classic.js b/compiled/facebook-www/ReactDOMServer-prod.classic.js index 57331a2dee5d0..0ab2f82f71d03 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.classic.js +++ b/compiled/facebook-www/ReactDOMServer-prod.classic.js @@ -3633,4 +3633,4 @@ exports.renderToString = function(children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-classic-84a0a171e-20221214"; +exports.version = "18.3.0-www-classic-7efa9e597-20221215"; diff --git a/compiled/facebook-www/ReactDOMServer-prod.modern.js b/compiled/facebook-www/ReactDOMServer-prod.modern.js index ba774de0b5e97..604e2ba9cf3de 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServer-prod.modern.js @@ -3546,4 +3546,4 @@ exports.renderToString = function(children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-modern-84a0a171e-20221214"; +exports.version = "18.3.0-www-modern-7efa9e597-20221215"; diff --git a/compiled/facebook-www/ReactDOMTesting-dev.classic.js b/compiled/facebook-www/ReactDOMTesting-dev.classic.js index 03d665cd54a52..6b89802cf2221 100644 --- a/compiled/facebook-www/ReactDOMTesting-dev.classic.js +++ b/compiled/facebook-www/ReactDOMTesting-dev.classic.js @@ -17533,7 +17533,14 @@ function throwException( } while (workInProgress !== null); } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows +// into a dehydrated boundary. + +var SelectiveHydrationException = new Error( + "This is not a real error. It's an implementation detail of React's " + + "selective hydration feature. If this leaks into userspace, it's a bug in " + + "React. Please file an issue." +); var didReceiveUpdate = false; var didWarnAboutBadClass; var didWarnAboutModulePatternComponent; @@ -19743,18 +19750,29 @@ function updateDehydratedSuspenseComponent( current, attemptHydrationAtLane, eventTime - ); - } - } // If we have scheduled higher pri work above, this will just abort the render - // since we now have higher priority work. We'll try to infinitely suspend until - // we yield. TODO: We could probably just force yielding earlier instead. + ); // Throw a special object that signals to the work loop that it should + // interrupt the current render. + // + // Because we're inside a React-only execution stack, we don't + // strictly need to throw here — we could instead modify some internal + // work loop state. But using an exception means we don't need to + // check for this case on every iteration of the work loop. So doing + // it this way moves the check out of the fast path. - renderDidSuspendDelayIfPossible(); // If we rendered synchronously, we won't yield so have to render something. - // This will cause us to delete any existing content. + throw SelectiveHydrationException; + } + } // If we did not selectively hydrate, we'll continue rendering without + // hydrating. Mark this tree as suspended to prevent it from committing + // outside a transition. + // + // This path should only happen if the hydration lane already suspended. + // Currently, it also happens during sync updates because there is no + // hydration lane for sync updates. // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. + renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( current, workInProgress, @@ -27166,7 +27184,8 @@ var SuspendedOnError = 1; var SuspendedOnData = 2; var SuspendedOnImmediate = 3; var SuspendedOnDeprecatedThrowPromise = 4; -var SuspendedAndReadyToUnwind = 5; // When this is true, the work-in-progress fiber just suspended (or errored) and +var SuspendedAndReadyToUnwind = 5; +var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and // we've yet to unwind the stack. In some cases, we may yield to the main thread // after this happens. If the fiber is pinged before we resume, we can retry // immediately instead of unwinding the stack. @@ -28252,6 +28271,30 @@ function getRenderLanes() { return renderLanes$1; } +function resetWorkInProgressStack() { + if (workInProgress === null) return; + var interruptedWork; + + if (workInProgressSuspendedReason === NotSuspended) { + // Normal case. Work-in-progress hasn't started yet. Unwind all + // its parents. + interruptedWork = workInProgress.return; + } else { + // Work-in-progress is in suspended state. Reset the work loop and unwind + // both the suspended fiber and all its parents. + resetSuspendedWorkLoopOnUnwind(); + interruptedWork = workInProgress; + } + + while (interruptedWork !== null) { + var current = interruptedWork.alternate; + unwindInterruptedWork(current, interruptedWork); + interruptedWork = interruptedWork.return; + } + + workInProgress = null; +} + function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = NoLanes; @@ -28265,27 +28308,7 @@ function prepareFreshStack(root, lanes) { cancelTimeout(timeoutHandle); } - if (workInProgress !== null) { - var interruptedWork; - - if (workInProgressSuspendedReason === NotSuspended) { - // Normal case. Work-in-progress hasn't started yet. Unwind all - // its parents. - interruptedWork = workInProgress.return; - } else { - // Work-in-progress is in suspended state. Reset the work loop and unwind - // both the suspended fiber and all its parents. - resetSuspendedWorkLoopOnUnwind(); - interruptedWork = workInProgress; - } - - while (interruptedWork !== null) { - var current = interruptedWork.alternate; - unwindInterruptedWork(current, interruptedWork); - interruptedWork = interruptedWork.return; - } - } - + resetWorkInProgressStack(); workInProgressRoot = root; var rootWorkInProgress = createWorkInProgress(root.current, null); workInProgress = rootWorkInProgress; @@ -28344,6 +28367,17 @@ function handleThrow(root, thrownValue) { workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves() ? SuspendedOnData : SuspendedOnImmediate; + } else if (thrownValue === SelectiveHydrationException) { + // An update flowed into a dehydrated boundary. Before we can apply the + // update, we need to finish hydrating. Interrupt the work-in-progress + // render so we can restart at the hydration lane. + // + // The ideal implementation would be able to switch contexts without + // unwinding the current stack. + // + // We could name this something more general but as of now it's the only + // case where we think this should happen. + workInProgressSuspendedReason = SuspendedOnHydration; } else { // This is a regular error. var isWakeable = @@ -28519,7 +28553,7 @@ function renderRootSync(root, lanes) { prepareFreshStack(root, lanes); } - do { + outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && @@ -28535,9 +28569,25 @@ function renderRootSync(root, lanes) { // function and fork the behavior some other way. var unitOfWork = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = NotSuspended; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(unitOfWork, thrownValue); // Continue with the normal work loop. + + switch (workInProgressSuspendedReason) { + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + + default: { + // Continue with the normal work loop. + workInProgressSuspendedReason = NotSuspended; + workInProgressThrownValue = null; + unwindSuspendedUnitOfWork(unitOfWork, thrownValue); + break; + } + } } workLoopSync(); @@ -28666,6 +28716,15 @@ function renderRootConcurrent(root, lanes) { break; } + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + default: { throw new Error( "Unexpected SuspendedReason. This is a bug in React." @@ -31101,7 +31160,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-classic-7efa9e597-20221215"; function createPortal( children, diff --git a/compiled/facebook-www/ReactDOMTesting-dev.modern.js b/compiled/facebook-www/ReactDOMTesting-dev.modern.js index 6d7f28586370f..a2f443c880259 100644 --- a/compiled/facebook-www/ReactDOMTesting-dev.modern.js +++ b/compiled/facebook-www/ReactDOMTesting-dev.modern.js @@ -24974,7 +24974,14 @@ function throwException( } while (workInProgress !== null); } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows +// into a dehydrated boundary. + +var SelectiveHydrationException = new Error( + "This is not a real error. It's an implementation detail of React's " + + "selective hydration feature. If this leaks into userspace, it's a bug in " + + "React. Please file an issue." +); var didReceiveUpdate = false; var didWarnAboutBadClass; var didWarnAboutModulePatternComponent; @@ -27168,18 +27175,29 @@ function updateDehydratedSuspenseComponent( current, attemptHydrationAtLane, eventTime - ); - } - } // If we have scheduled higher pri work above, this will just abort the render - // since we now have higher priority work. We'll try to infinitely suspend until - // we yield. TODO: We could probably just force yielding earlier instead. + ); // Throw a special object that signals to the work loop that it should + // interrupt the current render. + // + // Because we're inside a React-only execution stack, we don't + // strictly need to throw here — we could instead modify some internal + // work loop state. But using an exception means we don't need to + // check for this case on every iteration of the work loop. So doing + // it this way moves the check out of the fast path. - renderDidSuspendDelayIfPossible(); // If we rendered synchronously, we won't yield so have to render something. - // This will cause us to delete any existing content. + throw SelectiveHydrationException; + } + } // If we did not selectively hydrate, we'll continue rendering without + // hydrating. Mark this tree as suspended to prevent it from committing + // outside a transition. + // + // This path should only happen if the hydration lane already suspended. + // Currently, it also happens during sync updates because there is no + // hydration lane for sync updates. // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. + renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( current, workInProgress, @@ -34454,7 +34472,8 @@ var SuspendedOnError = 1; var SuspendedOnData = 2; var SuspendedOnImmediate = 3; var SuspendedOnDeprecatedThrowPromise = 4; -var SuspendedAndReadyToUnwind = 5; // When this is true, the work-in-progress fiber just suspended (or errored) and +var SuspendedAndReadyToUnwind = 5; +var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and // we've yet to unwind the stack. In some cases, we may yield to the main thread // after this happens. If the fiber is pinged before we resume, we can retry // immediately instead of unwinding the stack. @@ -35540,6 +35559,30 @@ function getRenderLanes() { return renderLanes$1; } +function resetWorkInProgressStack() { + if (workInProgress === null) return; + var interruptedWork; + + if (workInProgressSuspendedReason === NotSuspended) { + // Normal case. Work-in-progress hasn't started yet. Unwind all + // its parents. + interruptedWork = workInProgress.return; + } else { + // Work-in-progress is in suspended state. Reset the work loop and unwind + // both the suspended fiber and all its parents. + resetSuspendedWorkLoopOnUnwind(); + interruptedWork = workInProgress; + } + + while (interruptedWork !== null) { + var current = interruptedWork.alternate; + unwindInterruptedWork(current, interruptedWork); + interruptedWork = interruptedWork.return; + } + + workInProgress = null; +} + function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = NoLanes; @@ -35553,27 +35596,7 @@ function prepareFreshStack(root, lanes) { cancelTimeout(timeoutHandle); } - if (workInProgress !== null) { - var interruptedWork; - - if (workInProgressSuspendedReason === NotSuspended) { - // Normal case. Work-in-progress hasn't started yet. Unwind all - // its parents. - interruptedWork = workInProgress.return; - } else { - // Work-in-progress is in suspended state. Reset the work loop and unwind - // both the suspended fiber and all its parents. - resetSuspendedWorkLoopOnUnwind(); - interruptedWork = workInProgress; - } - - while (interruptedWork !== null) { - var current = interruptedWork.alternate; - unwindInterruptedWork(current, interruptedWork); - interruptedWork = interruptedWork.return; - } - } - + resetWorkInProgressStack(); workInProgressRoot = root; var rootWorkInProgress = createWorkInProgress(root.current, null); workInProgress = rootWorkInProgress; @@ -35632,6 +35655,17 @@ function handleThrow(root, thrownValue) { workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves() ? SuspendedOnData : SuspendedOnImmediate; + } else if (thrownValue === SelectiveHydrationException) { + // An update flowed into a dehydrated boundary. Before we can apply the + // update, we need to finish hydrating. Interrupt the work-in-progress + // render so we can restart at the hydration lane. + // + // The ideal implementation would be able to switch contexts without + // unwinding the current stack. + // + // We could name this something more general but as of now it's the only + // case where we think this should happen. + workInProgressSuspendedReason = SuspendedOnHydration; } else { // This is a regular error. var isWakeable = @@ -35807,7 +35841,7 @@ function renderRootSync(root, lanes) { prepareFreshStack(root, lanes); } - do { + outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && @@ -35823,9 +35857,25 @@ function renderRootSync(root, lanes) { // function and fork the behavior some other way. var unitOfWork = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = NotSuspended; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(unitOfWork, thrownValue); // Continue with the normal work loop. + + switch (workInProgressSuspendedReason) { + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + + default: { + // Continue with the normal work loop. + workInProgressSuspendedReason = NotSuspended; + workInProgressThrownValue = null; + unwindSuspendedUnitOfWork(unitOfWork, thrownValue); + break; + } + } } workLoopSync(); @@ -35954,6 +36004,15 @@ function renderRootConcurrent(root, lanes) { break; } + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + default: { throw new Error( "Unexpected SuspendedReason. This is a bug in React." @@ -38389,7 +38448,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-modern-7efa9e597-20221215"; function createPortal( children, diff --git a/compiled/facebook-www/ReactDOMTesting-prod.classic.js b/compiled/facebook-www/ReactDOMTesting-prod.classic.js index 7ed8e177fca27..c00cbdfe3752c 100644 --- a/compiled/facebook-www/ReactDOMTesting-prod.classic.js +++ b/compiled/facebook-www/ReactDOMTesting-prod.classic.js @@ -4720,6 +4720,7 @@ function markSuspenseBoundaryShouldCapture( return suspenseBoundary; } var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + SelectiveHydrationException = Error(formatProdErrorMessage(461)), didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -5590,11 +5591,14 @@ function updateDehydratedSuspenseComponent( 0 !== (suspenseInstance & (nextProps.suspendedLanes | renderLanes)) ? 0 : suspenseInstance; - 0 !== suspenseInstance && - suspenseInstance !== suspenseState.retryLane && - ((suspenseState.retryLane = suspenseInstance), + if ( + 0 !== suspenseInstance && + suspenseInstance !== suspenseState.retryLane + ) + throw ((suspenseState.retryLane = suspenseInstance), enqueueConcurrentRenderForLane(current, suspenseInstance), - scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1)); + scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1), + SelectiveHydrationException); } renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( @@ -9333,24 +9337,27 @@ function flushSync(fn) { 0 === (executionContext & 6) && flushSyncCallbacks(); } } +function resetWorkInProgressStack() { + if (null !== workInProgress) { + if (0 === workInProgressSuspendedReason) + var interruptedWork = workInProgress.return; + else + resetContextDependencies(), + resetHooksOnUnwind(), + (interruptedWork = workInProgress); + for (; null !== interruptedWork; ) + unwindInterruptedWork(interruptedWork.alternate, interruptedWork), + (interruptedWork = interruptedWork.return); + workInProgress = null; + } +} function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = 0; var timeoutHandle = root.timeoutHandle; -1 !== timeoutHandle && ((root.timeoutHandle = -1), cancelTimeout(timeoutHandle)); - if (null !== workInProgress) - for ( - 0 === workInProgressSuspendedReason - ? (timeoutHandle = workInProgress.return) - : (resetContextDependencies(), - resetHooksOnUnwind(), - (timeoutHandle = workInProgress)); - null !== timeoutHandle; - - ) - unwindInterruptedWork(timeoutHandle.alternate, timeoutHandle), - (timeoutHandle = timeoutHandle.return); + resetWorkInProgressStack(); workInProgressRoot = root; workInProgress = root = createWorkInProgress(root.current, null); workInProgressRootRenderLanes = renderLanes$1 = lanes; @@ -9377,9 +9384,11 @@ function handleThrow(root, thrownValue) { : 3; } else workInProgressSuspendedReason = - null !== thrownValue && - "object" === typeof thrownValue && - "function" === typeof thrownValue.then + thrownValue === SelectiveHydrationException + ? 6 + : null !== thrownValue && + "object" === typeof thrownValue && + "function" === typeof thrownValue.then ? 4 : 1; workInProgressThrownValue = thrownValue; @@ -9442,14 +9451,21 @@ function renderRootSync(root, lanes) { prevCacheDispatcher = pushCacheDispatcher(); if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) (workInProgressTransitions = null), prepareFreshStack(root, lanes); - do + a: do try { if (0 !== workInProgressSuspendedReason && null !== workInProgress) { lanes = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = 0; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(lanes, thrownValue); + switch (workInProgressSuspendedReason) { + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; + default: + (workInProgressSuspendedReason = 0), + (workInProgressThrownValue = null), + unwindSuspendedUnitOfWork(lanes, thrownValue); + } } workLoopSync(); break; @@ -9521,6 +9537,10 @@ function renderRootConcurrent(root, lanes) { workInProgressThrownValue = null; unwindSuspendedUnitOfWork(lanes, thrownValue); break; + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; default: throw Error(formatProdErrorMessage(462)); } @@ -11341,7 +11361,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1538 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-classic-84a0a171e-20221214", + version: "18.3.0-www-classic-7efa9e597-20221215", rendererPackageName: "react-dom" }; var internals$jscomp$inline_2070 = { @@ -11371,7 +11391,7 @@ var internals$jscomp$inline_2070 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-84a0a171e-20221214" + reconcilerVersion: "18.3.0-next-7efa9e597-20221215" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2071 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -14860,4 +14880,4 @@ exports.unstable_renderSubtreeIntoContainer = function( ); }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-next-84a0a171e-20221214"; +exports.version = "18.3.0-next-7efa9e597-20221215"; diff --git a/compiled/facebook-www/ReactDOMTesting-prod.modern.js b/compiled/facebook-www/ReactDOMTesting-prod.modern.js index 284b5a1209cf0..ee6027a4ff860 100644 --- a/compiled/facebook-www/ReactDOMTesting-prod.modern.js +++ b/compiled/facebook-www/ReactDOMTesting-prod.modern.js @@ -7712,6 +7712,7 @@ function markSuspenseBoundaryShouldCapture( return suspenseBoundary; } var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner, + SelectiveHydrationException = Error(formatProdErrorMessage(461)), didReceiveUpdate = !1; function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { workInProgress.child = @@ -8546,11 +8547,14 @@ function updateDehydratedSuspenseComponent( 0 !== (suspenseInstance & (nextProps.suspendedLanes | renderLanes)) ? 0 : suspenseInstance; - 0 !== suspenseInstance && - suspenseInstance !== suspenseState.retryLane && - ((suspenseState.retryLane = suspenseInstance), + if ( + 0 !== suspenseInstance && + suspenseInstance !== suspenseState.retryLane + ) + throw ((suspenseState.retryLane = suspenseInstance), enqueueConcurrentRenderForLane(current, suspenseInstance), - scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1)); + scheduleUpdateOnFiber(nextProps, current, suspenseInstance, -1), + SelectiveHydrationException); } renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( @@ -12227,24 +12231,27 @@ function flushSync(fn) { 0 === (executionContext & 6) && flushSyncCallbacks(); } } +function resetWorkInProgressStack() { + if (null !== workInProgress) { + if (0 === workInProgressSuspendedReason) + var interruptedWork = workInProgress.return; + else + resetContextDependencies(), + resetHooksOnUnwind(), + (interruptedWork = workInProgress); + for (; null !== interruptedWork; ) + unwindInterruptedWork(interruptedWork.alternate, interruptedWork), + (interruptedWork = interruptedWork.return); + workInProgress = null; + } +} function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = 0; var timeoutHandle = root.timeoutHandle; -1 !== timeoutHandle && ((root.timeoutHandle = -1), cancelTimeout(timeoutHandle)); - if (null !== workInProgress) - for ( - 0 === workInProgressSuspendedReason - ? (timeoutHandle = workInProgress.return) - : (resetContextDependencies(), - resetHooksOnUnwind(), - (timeoutHandle = workInProgress)); - null !== timeoutHandle; - - ) - unwindInterruptedWork(timeoutHandle.alternate, timeoutHandle), - (timeoutHandle = timeoutHandle.return); + resetWorkInProgressStack(); workInProgressRoot = root; workInProgress = root = createWorkInProgress(root.current, null); workInProgressRootRenderLanes = renderLanes$1 = lanes; @@ -12271,9 +12278,11 @@ function handleThrow(root, thrownValue) { : 3; } else workInProgressSuspendedReason = - null !== thrownValue && - "object" === typeof thrownValue && - "function" === typeof thrownValue.then + thrownValue === SelectiveHydrationException + ? 6 + : null !== thrownValue && + "object" === typeof thrownValue && + "function" === typeof thrownValue.then ? 4 : 1; workInProgressThrownValue = thrownValue; @@ -12336,14 +12345,21 @@ function renderRootSync(root, lanes) { prevCacheDispatcher = pushCacheDispatcher(); if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) (workInProgressTransitions = null), prepareFreshStack(root, lanes); - do + a: do try { if (0 !== workInProgressSuspendedReason && null !== workInProgress) { lanes = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = 0; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(lanes, thrownValue); + switch (workInProgressSuspendedReason) { + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; + default: + (workInProgressSuspendedReason = 0), + (workInProgressThrownValue = null), + unwindSuspendedUnitOfWork(lanes, thrownValue); + } } workLoopSync(); break; @@ -12415,6 +12431,10 @@ function renderRootConcurrent(root, lanes) { workInProgressThrownValue = null; unwindSuspendedUnitOfWork(lanes, thrownValue); break; + case 6: + resetWorkInProgressStack(); + workInProgressRootExitStatus = 6; + break a; default: throw Error(formatProdErrorMessage(462)); } @@ -13973,7 +13993,7 @@ Internals.Events = [ var devToolsConfig$jscomp$inline_1693 = { findFiberByHostInstance: getClosestInstanceFromNode, bundleType: 0, - version: "18.3.0-www-modern-84a0a171e-20221214", + version: "18.3.0-www-modern-7efa9e597-20221215", rendererPackageName: "react-dom" }; var internals$jscomp$inline_2095 = { @@ -14004,7 +14024,7 @@ var internals$jscomp$inline_2095 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-next-84a0a171e-20221214" + reconcilerVersion: "18.3.0-next-7efa9e597-20221215" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_2096 = __REACT_DEVTOOLS_GLOBAL_HOOK__; @@ -14327,4 +14347,4 @@ exports.unstable_flushControlled = function(fn) { } }; exports.unstable_runWithPriority = runWithPriority; -exports.version = "18.3.0-next-84a0a171e-20221214"; +exports.version = "18.3.0-next-7efa9e597-20221215"; diff --git a/compiled/facebook-www/ReactTestRenderer-dev.classic.js b/compiled/facebook-www/ReactTestRenderer-dev.classic.js index 379685e13910e..e42515d69e6c4 100644 --- a/compiled/facebook-www/ReactTestRenderer-dev.classic.js +++ b/compiled/facebook-www/ReactTestRenderer-dev.classic.js @@ -10997,7 +10997,14 @@ function throwException( } while (workInProgress !== null); } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows +// into a dehydrated boundary. + +var SelectiveHydrationException = new Error( + "This is not a real error. It's an implementation detail of React's " + + "selective hydration feature. If this leaks into userspace, it's a bug in " + + "React. Please file an issue." +); var didReceiveUpdate = false; var didWarnAboutBadClass; var didWarnAboutModulePatternComponent; @@ -12954,18 +12961,29 @@ function updateDehydratedSuspenseComponent( current, attemptHydrationAtLane, eventTime - ); - } - } // If we have scheduled higher pri work above, this will just abort the render - // since we now have higher priority work. We'll try to infinitely suspend until - // we yield. TODO: We could probably just force yielding earlier instead. + ); // Throw a special object that signals to the work loop that it should + // interrupt the current render. + // + // Because we're inside a React-only execution stack, we don't + // strictly need to throw here — we could instead modify some internal + // work loop state. But using an exception means we don't need to + // check for this case on every iteration of the work loop. So doing + // it this way moves the check out of the fast path. - renderDidSuspendDelayIfPossible(); // If we rendered synchronously, we won't yield so have to render something. - // This will cause us to delete any existing content. + throw SelectiveHydrationException; + } + } // If we did not selectively hydrate, we'll continue rendering without + // hydrating. Mark this tree as suspended to prevent it from committing + // outside a transition. + // + // This path should only happen if the hydration lane already suspended. + // Currently, it also happens during sync updates because there is no + // hydration lane for sync updates. // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. + renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( current, workInProgress, @@ -19823,7 +19841,8 @@ var SuspendedOnError = 1; var SuspendedOnData = 2; var SuspendedOnImmediate = 3; var SuspendedOnDeprecatedThrowPromise = 4; -var SuspendedAndReadyToUnwind = 5; // When this is true, the work-in-progress fiber just suspended (or errored) and +var SuspendedAndReadyToUnwind = 5; +var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and // we've yet to unwind the stack. In some cases, we may yield to the main thread // after this happens. If the fiber is pinged before we resume, we can retry // immediately instead of unwinding the stack. @@ -20829,6 +20848,30 @@ function getRenderLanes() { return renderLanes$1; } +function resetWorkInProgressStack() { + if (workInProgress === null) return; + var interruptedWork; + + if (workInProgressSuspendedReason === NotSuspended) { + // Normal case. Work-in-progress hasn't started yet. Unwind all + // its parents. + interruptedWork = workInProgress.return; + } else { + // Work-in-progress is in suspended state. Reset the work loop and unwind + // both the suspended fiber and all its parents. + resetSuspendedWorkLoopOnUnwind(); + interruptedWork = workInProgress; + } + + while (interruptedWork !== null) { + var current = interruptedWork.alternate; + unwindInterruptedWork(current, interruptedWork); + interruptedWork = interruptedWork.return; + } + + workInProgress = null; +} + function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = NoLanes; @@ -20842,27 +20885,7 @@ function prepareFreshStack(root, lanes) { cancelTimeout(timeoutHandle); } - if (workInProgress !== null) { - var interruptedWork; - - if (workInProgressSuspendedReason === NotSuspended) { - // Normal case. Work-in-progress hasn't started yet. Unwind all - // its parents. - interruptedWork = workInProgress.return; - } else { - // Work-in-progress is in suspended state. Reset the work loop and unwind - // both the suspended fiber and all its parents. - resetSuspendedWorkLoopOnUnwind(); - interruptedWork = workInProgress; - } - - while (interruptedWork !== null) { - var current = interruptedWork.alternate; - unwindInterruptedWork(current, interruptedWork); - interruptedWork = interruptedWork.return; - } - } - + resetWorkInProgressStack(); workInProgressRoot = root; var rootWorkInProgress = createWorkInProgress(root.current, null); workInProgress = rootWorkInProgress; @@ -20921,6 +20944,17 @@ function handleThrow(root, thrownValue) { workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves() ? SuspendedOnData : SuspendedOnImmediate; + } else if (thrownValue === SelectiveHydrationException) { + // An update flowed into a dehydrated boundary. Before we can apply the + // update, we need to finish hydrating. Interrupt the work-in-progress + // render so we can restart at the hydration lane. + // + // The ideal implementation would be able to switch contexts without + // unwinding the current stack. + // + // We could name this something more general but as of now it's the only + // case where we think this should happen. + workInProgressSuspendedReason = SuspendedOnHydration; } else { // This is a regular error. var isWakeable = @@ -21101,7 +21135,7 @@ function renderRootSync(root, lanes) { prepareFreshStack(root, lanes); } - do { + outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && @@ -21117,9 +21151,25 @@ function renderRootSync(root, lanes) { // function and fork the behavior some other way. var unitOfWork = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = NotSuspended; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(unitOfWork, thrownValue); // Continue with the normal work loop. + + switch (workInProgressSuspendedReason) { + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + + default: { + // Continue with the normal work loop. + workInProgressSuspendedReason = NotSuspended; + workInProgressThrownValue = null; + unwindSuspendedUnitOfWork(unitOfWork, thrownValue); + break; + } + } } workLoopSync(); @@ -21248,6 +21298,15 @@ function renderRootConcurrent(root, lanes) { break; } + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + default: { throw new Error( "Unexpected SuspendedReason. This is a bug in React." @@ -23801,7 +23860,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-classic-7efa9e597-20221215"; var didWarnAboutNestedUpdates; diff --git a/compiled/facebook-www/ReactTestRenderer-dev.modern.js b/compiled/facebook-www/ReactTestRenderer-dev.modern.js index 3ae539a18eb14..9dac5bfd36714 100644 --- a/compiled/facebook-www/ReactTestRenderer-dev.modern.js +++ b/compiled/facebook-www/ReactTestRenderer-dev.modern.js @@ -10997,7 +10997,14 @@ function throwException( } while (workInProgress !== null); } -var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; +var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows +// into a dehydrated boundary. + +var SelectiveHydrationException = new Error( + "This is not a real error. It's an implementation detail of React's " + + "selective hydration feature. If this leaks into userspace, it's a bug in " + + "React. Please file an issue." +); var didReceiveUpdate = false; var didWarnAboutBadClass; var didWarnAboutModulePatternComponent; @@ -12954,18 +12961,29 @@ function updateDehydratedSuspenseComponent( current, attemptHydrationAtLane, eventTime - ); - } - } // If we have scheduled higher pri work above, this will just abort the render - // since we now have higher priority work. We'll try to infinitely suspend until - // we yield. TODO: We could probably just force yielding earlier instead. + ); // Throw a special object that signals to the work loop that it should + // interrupt the current render. + // + // Because we're inside a React-only execution stack, we don't + // strictly need to throw here — we could instead modify some internal + // work loop state. But using an exception means we don't need to + // check for this case on every iteration of the work loop. So doing + // it this way moves the check out of the fast path. - renderDidSuspendDelayIfPossible(); // If we rendered synchronously, we won't yield so have to render something. - // This will cause us to delete any existing content. + throw SelectiveHydrationException; + } + } // If we did not selectively hydrate, we'll continue rendering without + // hydrating. Mark this tree as suspended to prevent it from committing + // outside a transition. + // + // This path should only happen if the hydration lane already suspended. + // Currently, it also happens during sync updates because there is no + // hydration lane for sync updates. // TODO: We should ideally have a sync hydration lane that we can apply to do // a pass where we hydrate this subtree in place using the previous Context and then // reapply the update afterwards. + renderDidSuspendDelayIfPossible(); return retrySuspenseComponentWithoutHydrating( current, workInProgress, @@ -19823,7 +19841,8 @@ var SuspendedOnError = 1; var SuspendedOnData = 2; var SuspendedOnImmediate = 3; var SuspendedOnDeprecatedThrowPromise = 4; -var SuspendedAndReadyToUnwind = 5; // When this is true, the work-in-progress fiber just suspended (or errored) and +var SuspendedAndReadyToUnwind = 5; +var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and // we've yet to unwind the stack. In some cases, we may yield to the main thread // after this happens. If the fiber is pinged before we resume, we can retry // immediately instead of unwinding the stack. @@ -20829,6 +20848,30 @@ function getRenderLanes() { return renderLanes$1; } +function resetWorkInProgressStack() { + if (workInProgress === null) return; + var interruptedWork; + + if (workInProgressSuspendedReason === NotSuspended) { + // Normal case. Work-in-progress hasn't started yet. Unwind all + // its parents. + interruptedWork = workInProgress.return; + } else { + // Work-in-progress is in suspended state. Reset the work loop and unwind + // both the suspended fiber and all its parents. + resetSuspendedWorkLoopOnUnwind(); + interruptedWork = workInProgress; + } + + while (interruptedWork !== null) { + var current = interruptedWork.alternate; + unwindInterruptedWork(current, interruptedWork); + interruptedWork = interruptedWork.return; + } + + workInProgress = null; +} + function prepareFreshStack(root, lanes) { root.finishedWork = null; root.finishedLanes = NoLanes; @@ -20842,27 +20885,7 @@ function prepareFreshStack(root, lanes) { cancelTimeout(timeoutHandle); } - if (workInProgress !== null) { - var interruptedWork; - - if (workInProgressSuspendedReason === NotSuspended) { - // Normal case. Work-in-progress hasn't started yet. Unwind all - // its parents. - interruptedWork = workInProgress.return; - } else { - // Work-in-progress is in suspended state. Reset the work loop and unwind - // both the suspended fiber and all its parents. - resetSuspendedWorkLoopOnUnwind(); - interruptedWork = workInProgress; - } - - while (interruptedWork !== null) { - var current = interruptedWork.alternate; - unwindInterruptedWork(current, interruptedWork); - interruptedWork = interruptedWork.return; - } - } - + resetWorkInProgressStack(); workInProgressRoot = root; var rootWorkInProgress = createWorkInProgress(root.current, null); workInProgress = rootWorkInProgress; @@ -20921,6 +20944,17 @@ function handleThrow(root, thrownValue) { workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves() ? SuspendedOnData : SuspendedOnImmediate; + } else if (thrownValue === SelectiveHydrationException) { + // An update flowed into a dehydrated boundary. Before we can apply the + // update, we need to finish hydrating. Interrupt the work-in-progress + // render so we can restart at the hydration lane. + // + // The ideal implementation would be able to switch contexts without + // unwinding the current stack. + // + // We could name this something more general but as of now it's the only + // case where we think this should happen. + workInProgressSuspendedReason = SuspendedOnHydration; } else { // This is a regular error. var isWakeable = @@ -21101,7 +21135,7 @@ function renderRootSync(root, lanes) { prepareFreshStack(root, lanes); } - do { + outer: do { try { if ( workInProgressSuspendedReason !== NotSuspended && @@ -21117,9 +21151,25 @@ function renderRootSync(root, lanes) { // function and fork the behavior some other way. var unitOfWork = workInProgress; var thrownValue = workInProgressThrownValue; - workInProgressSuspendedReason = NotSuspended; - workInProgressThrownValue = null; - unwindSuspendedUnitOfWork(unitOfWork, thrownValue); // Continue with the normal work loop. + + switch (workInProgressSuspendedReason) { + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + + default: { + // Continue with the normal work loop. + workInProgressSuspendedReason = NotSuspended; + workInProgressThrownValue = null; + unwindSuspendedUnitOfWork(unitOfWork, thrownValue); + break; + } + } } workLoopSync(); @@ -21248,6 +21298,15 @@ function renderRootConcurrent(root, lanes) { break; } + case SuspendedOnHydration: { + // Selective hydration. An update flowed into a dehydrated tree. + // Interrupt the current render so the work loop can switch to the + // hydration lane. + resetWorkInProgressStack(); + workInProgressRootExitStatus = RootDidNotComplete; + break outer; + } + default: { throw new Error( "Unexpected SuspendedReason. This is a bug in React." @@ -23801,7 +23860,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-modern-84a0a171e-20221214"; +var ReactVersion = "18.3.0-www-modern-7efa9e597-20221215"; var didWarnAboutNestedUpdates;