Skip to content

Commit

Permalink
Revert "Force unwind work loop during selective hydration (#25695)"
Browse files Browse the repository at this point in the history
This reverts commit 44c4e6f.
  • Loading branch information
tyao1 committed Dec 5, 2022
1 parent 3168708 commit 57199f7
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1496,10 +1496,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Start rendering. This will force the first boundary to hydrate
// by scheduling it at one higher pri than Idle.
expect(Scheduler).toFlushAndYieldThrough([
// An update was scheduled to force hydrate the boundary, but React will
// continue rendering at Idle until the next time React yields. This is
// fine though because it will switch to the hydration level when it
// re-enters the work loop.
'App',

// Start hydrating A
'A',
'AA',
]);

// Hover over A which (could) schedule at one higher pri than Idle.
Expand Down
32 changes: 6 additions & 26 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,14 +282,6 @@ import {

const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;

// A special exception that's used to unwind the stack when an update flows
// into a dehydrated boundary.
export const SelectiveHydrationException: mixed = 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.',
);

let didReceiveUpdate: boolean = false;

let didWarnAboutBadClass;
Expand Down Expand Up @@ -2868,16 +2860,6 @@ function updateDehydratedSuspenseComponent(
attemptHydrationAtLane,
eventTime,
);

// 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.
throw SelectiveHydrationException;
} else {
// We have already tried to ping at a higher priority than we're rendering with
// so if we got here, we must have failed to hydrate at those levels. We must
Expand All @@ -2888,17 +2870,15 @@ function updateDehydratedSuspenseComponent(
}
}

// 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.
// 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.
renderDidSuspendDelayIfPossible();
// If we rendered synchronously, we won't yield so have to render something.
// This will cause us to delete any existing content.
// 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,
Expand Down
50 changes: 7 additions & 43 deletions packages/react-reconciler/src/ReactFiberWorkLoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ import {
} from './ReactEventPriorities';
import {requestCurrentTransition, NoTransition} from './ReactFiberTransition';
import {
SelectiveHydrationException,
beginWork as originalBeginWork,
replayFunctionComponent,
} from './ReactFiberBeginWork';
Expand Down Expand Up @@ -317,14 +316,13 @@ let workInProgress: Fiber | null = null;
// The lanes we're rendering
let workInProgressRootRenderLanes: Lanes = NoLanes;

opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5 | 6;
opaque type SuspendedReason = 0 | 1 | 2 | 3 | 4 | 5;
const NotSuspended: SuspendedReason = 0;
const SuspendedOnError: SuspendedReason = 1;
const SuspendedOnData: SuspendedReason = 2;
const SuspendedOnImmediate: SuspendedReason = 3;
const SuspendedOnDeprecatedThrowPromise: SuspendedReason = 4;
const SuspendedAndReadyToUnwind: SuspendedReason = 5;
const SuspendedOnHydration: SuspendedReason = 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
Expand Down Expand Up @@ -1799,17 +1797,6 @@ function handleThrow(root, thrownValue): void {
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.
const isWakeable =
Expand Down Expand Up @@ -2009,9 +1996,6 @@ export function renderHasNotSuspendedYet(): boolean {
return workInProgressRootExitStatus === RootInProgress;
}

// TODO: Over time, this function and renderRootConcurrent have become more
// and more similar. Not sure it makes sense to maintain forked paths. Consider
// unifying them again.
function renderRootSync(root: FiberRoot, lanes: Lanes) {
const prevExecutionContext = executionContext;
executionContext |= RenderContext;
Expand Down Expand Up @@ -2051,7 +2035,7 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
markRenderStarted(lanes);
}

outer: do {
do {
try {
if (
workInProgressSuspendedReason !== NotSuspended &&
Expand All @@ -2067,23 +2051,11 @@ function renderRootSync(root: FiberRoot, lanes: Lanes) {
// function and fork the behavior some other way.
const unitOfWork = workInProgress;
const thrownValue = workInProgressThrownValue;
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.
workInProgress = null;
workInProgressRootExitStatus = RootDidNotComplete;
break outer;
}
default: {
// Continue with the normal work loop.
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
break;
}
}
workInProgressSuspendedReason = NotSuspended;
workInProgressThrownValue = null;
unwindSuspendedUnitOfWork(unitOfWork, thrownValue);

// Continue with the normal work loop.
}
workLoopSync();
break;
Expand Down Expand Up @@ -2241,14 +2213,6 @@ function renderRootConcurrent(root: FiberRoot, lanes: Lanes) {
unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
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.
workInProgress = null;
workInProgressRootExitStatus = RootDidNotComplete;
break outer;
}
default: {
throw new Error(
'Unexpected SuspendedReason. This is a bug in React.',
Expand Down

0 comments on commit 57199f7

Please sign in to comment.