mirror of
https://github.com/facebook/react.git
synced 2026-02-26 18:58:05 +00:00
* Move dehydrated to be child of regular SuspenseComponent We now store the comment node on SuspenseState instead and that indicates that this SuspenseComponent is still dehydrated. We also store a child but that is only used to represent the DOM node for deletions and getNextHostSibling. * Move logic from DehydratedSuspenseComponent to SuspenseComponent Forked based on SuspenseState.dehydrated instead. * Retry logic for dehydrated boundary We can now simplify the logic for retrying dehydrated boundaries without hydrating. This is becomes simply a reconciliation against the dehydrated fragment which gets deleted, and the new children gets inserted. * Remove dehydrated from throw Instead we use the regular Suspense path. To save code, we attach retry listeners in the commit phase even though technically we don't have to. * Pop to nearest Suspense I think this is right...? * Popping hydration state should skip past the dehydrated instance * Split mount from update and special case suspended second pass The DidCapture flag isn't used consistently in the same way. We need further refactor for this. * Reorganize update path If we remove the dehydration status in the first pass and then do a second pass because we suspended, then we need to continue as if it didn't previously suspend. Since there is no fragment child etc. However, we must readd the deletion. * Schedule context work on the boundary and not the child * Warn for Suspense hydration in legacy mode It does a two pass render that client renders the content. * Rename DehydratedSuspenseComponent -> DehydratedFragment This now doesn't represent a suspense boundary itself. Its parent does. This Fiber represents the fragment around the dehydrated content. * Refactor returns Avoids the temporary mutable variables. I kept losing track of them. * Add a comment explaining the type. Placing it in the type since that's the central point as opposed to spread out.
147 lines
4.4 KiB
JavaScript
147 lines
4.4 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {Fiber} from './ReactFiber';
|
|
import type {ExpirationTime} from './ReactFiberExpirationTime';
|
|
import type {SuspenseState} from './ReactFiberSuspenseComponent';
|
|
|
|
import {
|
|
ClassComponent,
|
|
HostRoot,
|
|
HostComponent,
|
|
HostPortal,
|
|
ContextProvider,
|
|
SuspenseComponent,
|
|
SuspenseListComponent,
|
|
} from 'shared/ReactWorkTags';
|
|
import {DidCapture, NoEffect, ShouldCapture} from 'shared/ReactSideEffectTags';
|
|
import {enableSuspenseServerRenderer} from 'shared/ReactFeatureFlags';
|
|
|
|
import {popHostContainer, popHostContext} from './ReactFiberHostContext';
|
|
import {popSuspenseContext} from './ReactFiberSuspenseContext';
|
|
import {resetHydrationState} from './ReactFiberHydrationContext';
|
|
import {
|
|
isContextProvider as isLegacyContextProvider,
|
|
popContext as popLegacyContext,
|
|
popTopLevelContextObject as popTopLevelLegacyContextObject,
|
|
} from './ReactFiberContext';
|
|
import {popProvider} from './ReactFiberNewContext';
|
|
|
|
import invariant from 'shared/invariant';
|
|
|
|
function unwindWork(
|
|
workInProgress: Fiber,
|
|
renderExpirationTime: ExpirationTime,
|
|
) {
|
|
switch (workInProgress.tag) {
|
|
case ClassComponent: {
|
|
const Component = workInProgress.type;
|
|
if (isLegacyContextProvider(Component)) {
|
|
popLegacyContext(workInProgress);
|
|
}
|
|
const effectTag = workInProgress.effectTag;
|
|
if (effectTag & ShouldCapture) {
|
|
workInProgress.effectTag = (effectTag & ~ShouldCapture) | DidCapture;
|
|
return workInProgress;
|
|
}
|
|
return null;
|
|
}
|
|
case HostRoot: {
|
|
popHostContainer(workInProgress);
|
|
popTopLevelLegacyContextObject(workInProgress);
|
|
const effectTag = workInProgress.effectTag;
|
|
invariant(
|
|
(effectTag & DidCapture) === NoEffect,
|
|
'The root failed to unmount after an error. This is likely a bug in ' +
|
|
'React. Please file an issue.',
|
|
);
|
|
workInProgress.effectTag = (effectTag & ~ShouldCapture) | DidCapture;
|
|
return workInProgress;
|
|
}
|
|
case HostComponent: {
|
|
// TODO: popHydrationState
|
|
popHostContext(workInProgress);
|
|
return null;
|
|
}
|
|
case SuspenseComponent: {
|
|
popSuspenseContext(workInProgress);
|
|
if (enableSuspenseServerRenderer) {
|
|
const suspenseState: null | SuspenseState =
|
|
workInProgress.memoizedState;
|
|
if (suspenseState !== null && suspenseState.dehydrated !== null) {
|
|
invariant(
|
|
workInProgress.alternate !== null,
|
|
'Threw in newly mounted dehydrated component. This is likely a bug in ' +
|
|
'React. Please file an issue.',
|
|
);
|
|
resetHydrationState();
|
|
}
|
|
}
|
|
const effectTag = workInProgress.effectTag;
|
|
if (effectTag & ShouldCapture) {
|
|
workInProgress.effectTag = (effectTag & ~ShouldCapture) | DidCapture;
|
|
// Captured a suspense effect. Re-render the boundary.
|
|
return workInProgress;
|
|
}
|
|
return null;
|
|
}
|
|
case SuspenseListComponent: {
|
|
popSuspenseContext(workInProgress);
|
|
// SuspenseList doesn't actually catch anything. It should've been
|
|
// caught by a nested boundary. If not, it should bubble through.
|
|
return null;
|
|
}
|
|
case HostPortal:
|
|
popHostContainer(workInProgress);
|
|
return null;
|
|
case ContextProvider:
|
|
popProvider(workInProgress);
|
|
return null;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function unwindInterruptedWork(interruptedWork: Fiber) {
|
|
switch (interruptedWork.tag) {
|
|
case ClassComponent: {
|
|
const childContextTypes = interruptedWork.type.childContextTypes;
|
|
if (childContextTypes !== null && childContextTypes !== undefined) {
|
|
popLegacyContext(interruptedWork);
|
|
}
|
|
break;
|
|
}
|
|
case HostRoot: {
|
|
popHostContainer(interruptedWork);
|
|
popTopLevelLegacyContextObject(interruptedWork);
|
|
break;
|
|
}
|
|
case HostComponent: {
|
|
popHostContext(interruptedWork);
|
|
break;
|
|
}
|
|
case HostPortal:
|
|
popHostContainer(interruptedWork);
|
|
break;
|
|
case SuspenseComponent:
|
|
popSuspenseContext(interruptedWork);
|
|
break;
|
|
case SuspenseListComponent:
|
|
popSuspenseContext(interruptedWork);
|
|
break;
|
|
case ContextProvider:
|
|
popProvider(interruptedWork);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
export {unwindWork, unwindInterruptedWork};
|