Skip to content

Commit

Permalink
Land enableClientRenderFallbackOnTextMismatch flag
Browse files Browse the repository at this point in the history
This flag is already enabled on all relevant surfaces. We can remove it.
  • Loading branch information
acdlite committed Apr 20, 2022
1 parent 4175f05 commit 1f38d38
Show file tree
Hide file tree
Showing 26 changed files with 473 additions and 605 deletions.
37 changes: 0 additions & 37 deletions packages/react-dom/src/__tests__/ReactServerRendering-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ let React;
let ReactDOMServer;
let PropTypes;
let ReactCurrentDispatcher;
const enableSuspenseServerRenderer = require('shared/ReactFeatureFlags')
.enableSuspenseServerRenderer;

describe('ReactDOMServer', () => {
beforeEach(() => {
Expand Down Expand Up @@ -678,41 +676,6 @@ describe('ReactDOMServer', () => {
expect(markup).toBe('<div></div>');
});

if (!enableSuspenseServerRenderer) {
it('throws for unsupported types on the server', () => {
expect(() => {
ReactDOMServer.renderToString(<React.Suspense />);
}).toThrow('ReactDOMServer does not yet support Suspense.');

async function fakeImport(result) {
return {default: result};
}

expect(() => {
const LazyFoo = React.lazy(() =>
fakeImport(
new Promise(resolve =>
resolve(function Foo() {
return <div />;
}),
),
),
);
ReactDOMServer.renderToString(<LazyFoo />);
}).toThrow('ReactDOMServer does not yet support Suspense.');
});

it('throws when suspending on the server', () => {
function AsyncFoo() {
throw new Promise(() => {});
}

expect(() => {
ReactDOMServer.renderToString(<AsyncFoo />);
}).toThrow('ReactDOMServer does not yet support Suspense.');
});
}

it('does not get confused by throwing null', () => {
function Bad() {
// eslint-disable-next-line no-throw-literal
Expand Down
25 changes: 11 additions & 14 deletions packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ import {retryIfBlockedOn} from '../events/ReactDOMEventReplaying';

import {
enableClientRenderFallbackOnHydrationMismatch,
enableSuspenseServerRenderer,
enableCreateEventHandleAPI,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
Expand Down Expand Up @@ -747,19 +746,17 @@ function getNextHydratable(node) {
if (nodeType === ELEMENT_NODE || nodeType === TEXT_NODE) {
break;
}
if (enableSuspenseServerRenderer) {
if (nodeType === COMMENT_NODE) {
const nodeData = (node: any).data;
if (
nodeData === SUSPENSE_START_DATA ||
nodeData === SUSPENSE_FALLBACK_START_DATA ||
nodeData === SUSPENSE_PENDING_START_DATA
) {
break;
}
if (nodeData === SUSPENSE_END_DATA) {
return null;
}
if (nodeType === COMMENT_NODE) {
const nodeData = (node: any).data;
if (
nodeData === SUSPENSE_START_DATA ||
nodeData === SUSPENSE_FALLBACK_START_DATA ||
nodeData === SUSPENSE_PENDING_START_DATA
) {
break;
}
if (nodeData === SUSPENSE_END_DATA) {
return null;
}
}
}
Expand Down
85 changes: 38 additions & 47 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import {
warnAboutDeprecatedLifecycles,
disableLegacyContext,
disableModulePatternComponents,
enableSuspenseServerRenderer,
enableScopeAPI,
} from 'shared/ReactFeatureFlags';
import {
Expand Down Expand Up @@ -965,21 +964,17 @@ class ReactDOMServerRenderer {
outBuffer += this.render(child, frame.context, frame.domNamespace);
} catch (err) {
if (err != null && typeof err.then === 'function') {
if (enableSuspenseServerRenderer) {
if (this.suspenseDepth <= 0) {
throw new Error(
// TODO: include component name. This is a bit tricky with current factoring.
'A React component suspended while rendering, but no fallback UI was specified.\n' +
'\n' +
'Add a <Suspense fallback=...> component higher in the tree to ' +
'provide a loading indicator or placeholder to display.',
);
}

suspended = true;
} else {
throw new Error('ReactDOMServer does not yet support Suspense.');
if (this.suspenseDepth <= 0) {
throw new Error(
// TODO: include component name. This is a bit tricky with current factoring.
'A React component suspended while rendering, but no fallback UI was specified.\n' +
'\n' +
'Add a <Suspense fallback=...> component higher in the tree to ' +
'provide a loading indicator or placeholder to display.',
);
}

suspended = true;
} else {
throw err;
}
Expand Down Expand Up @@ -1097,39 +1092,35 @@ class ReactDOMServerRenderer {
return '';
}
case REACT_SUSPENSE_TYPE: {
if (enableSuspenseServerRenderer) {
const fallback = ((nextChild: any): ReactElement).props.fallback;
const fallbackChildren = toArray(fallback);
const nextChildren = toArray(
((nextChild: any): ReactElement).props.children,
);
const fallbackFrame: Frame = {
type: null,
domNamespace: parentNamespace,
children: fallbackChildren,
childIndex: 0,
context: context,
footer: '<!--/$-->',
};
const frame: Frame = {
fallbackFrame,
type: REACT_SUSPENSE_TYPE,
domNamespace: parentNamespace,
children: nextChildren,
childIndex: 0,
context: context,
footer: '<!--/$-->',
};
if (__DEV__) {
((frame: any): FrameDev).debugElementStack = [];
((fallbackFrame: any): FrameDev).debugElementStack = [];
}
this.stack.push(frame);
this.suspenseDepth++;
return '<!--$-->';
} else {
throw new Error('ReactDOMServer does not yet support Suspense.');
const fallback = ((nextChild: any): ReactElement).props.fallback;
const fallbackChildren = toArray(fallback);
const nextChildren = toArray(
((nextChild: any): ReactElement).props.children,
);
const fallbackFrame: Frame = {
type: null,
domNamespace: parentNamespace,
children: fallbackChildren,
childIndex: 0,
context: context,
footer: '<!--/$-->',
};
const frame: Frame = {
fallbackFrame,
type: REACT_SUSPENSE_TYPE,
domNamespace: parentNamespace,
children: nextChildren,
childIndex: 0,
context: context,
footer: '<!--/$-->',
};
if (__DEV__) {
((frame: any): FrameDev).debugElementStack = [];
((fallbackFrame: any): FrameDev).debugElementStack = [];
}
this.stack.push(frame);
this.suspenseDepth++;
return '<!--$-->';
}
// eslint-disable-next-line-no-fallthrough
case REACT_SCOPE_TYPE: {
Expand Down
151 changes: 72 additions & 79 deletions packages/react-reconciler/src/ReactFiberBeginWork.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ import {
disableModulePatternComponents,
enableProfilerCommitHooks,
enableProfilerTimer,
enableSuspenseServerRenderer,
warnAboutDefaultPropsOnFunctionComponents,
enableScopeAPI,
enableCache,
Expand Down Expand Up @@ -2134,17 +2133,15 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) {
// If we're currently hydrating, try to hydrate this boundary.
tryToClaimNextHydratableInstance(workInProgress);
// This could've been a dehydrated suspense component.
if (enableSuspenseServerRenderer) {
const suspenseState: null | SuspenseState = workInProgress.memoizedState;
if (suspenseState !== null) {
const dehydrated = suspenseState.dehydrated;
if (dehydrated !== null) {
return mountDehydratedSuspenseComponent(
workInProgress,
dehydrated,
renderLanes,
);
}
const suspenseState: null | SuspenseState = workInProgress.memoizedState;
if (suspenseState !== null) {
const dehydrated = suspenseState.dehydrated;
if (dehydrated !== null) {
return mountDehydratedSuspenseComponent(
workInProgress,
dehydrated,
renderLanes,
);
}
}

Expand Down Expand Up @@ -2220,59 +2217,57 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) {
// The current tree is already showing a fallback

// Special path for hydration
if (enableSuspenseServerRenderer) {
const dehydrated = prevState.dehydrated;
if (dehydrated !== null) {
if (!didSuspend) {
return updateDehydratedSuspenseComponent(
current,
workInProgress,
dehydrated,
prevState,
renderLanes,
);
} else if (workInProgress.flags & ForceClientRender) {
// Something errored during hydration. Try again without hydrating.
workInProgress.flags &= ~ForceClientRender;
return retrySuspenseComponentWithoutHydrating(
current,
workInProgress,
renderLanes,
new Error(
'There was an error while hydrating this Suspense boundary. ' +
'Switched to client rendering.',
),
);
} else if (
(workInProgress.memoizedState: null | SuspenseState) !== null
) {
// Something suspended and we should still be in dehydrated mode.
// Leave the existing child in place.
workInProgress.child = current.child;
// The dehydrated completion pass expects this flag to be there
// but the normal suspense pass doesn't.
workInProgress.flags |= DidCapture;
return null;
} else {
// Suspended but we should no longer be in dehydrated mode.
// Therefore we now have to render the fallback.
renderDidSuspendDelayIfPossible();
const nextPrimaryChildren = nextProps.children;
const nextFallbackChildren = nextProps.fallback;
const fallbackChildFragment = mountSuspenseFallbackAfterRetryWithoutHydrating(
current,
workInProgress,
nextPrimaryChildren,
nextFallbackChildren,
renderLanes,
);
const primaryChildFragment: Fiber = (workInProgress.child: any);
primaryChildFragment.memoizedState = mountSuspenseOffscreenState(
renderLanes,
);
workInProgress.memoizedState = SUSPENDED_MARKER;
return fallbackChildFragment;
}
const dehydrated = prevState.dehydrated;
if (dehydrated !== null) {
if (!didSuspend) {
return updateDehydratedSuspenseComponent(
current,
workInProgress,
dehydrated,
prevState,
renderLanes,
);
} else if (workInProgress.flags & ForceClientRender) {
// Something errored during hydration. Try again without hydrating.
workInProgress.flags &= ~ForceClientRender;
return retrySuspenseComponentWithoutHydrating(
current,
workInProgress,
renderLanes,
new Error(
'There was an error while hydrating this Suspense boundary. ' +
'Switched to client rendering.',
),
);
} else if (
(workInProgress.memoizedState: null | SuspenseState) !== null
) {
// Something suspended and we should still be in dehydrated mode.
// Leave the existing child in place.
workInProgress.child = current.child;
// The dehydrated completion pass expects this flag to be there
// but the normal suspense pass doesn't.
workInProgress.flags |= DidCapture;
return null;
} else {
// Suspended but we should no longer be in dehydrated mode.
// Therefore we now have to render the fallback.
renderDidSuspendDelayIfPossible();
const nextPrimaryChildren = nextProps.children;
const nextFallbackChildren = nextProps.fallback;
const fallbackChildFragment = mountSuspenseFallbackAfterRetryWithoutHydrating(
current,
workInProgress,
nextPrimaryChildren,
nextFallbackChildren,
renderLanes,
);
const primaryChildFragment: Fiber = (workInProgress.child: any);
primaryChildFragment.memoizedState = mountSuspenseOffscreenState(
renderLanes,
);
workInProgress.memoizedState = SUSPENDED_MARKER;
return fallbackChildFragment;
}
}

Expand Down Expand Up @@ -3657,20 +3652,18 @@ function attemptEarlyBailoutIfNoScheduledUpdate(
case SuspenseComponent: {
const state: SuspenseState | null = workInProgress.memoizedState;
if (state !== null) {
if (enableSuspenseServerRenderer) {
if (state.dehydrated !== null) {
pushSuspenseContext(
workInProgress,
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
);
// We know that this component will suspend again because if it has
// been unsuspended it has committed as a resolved Suspense component.
// If it needs to be retried, it should have work scheduled on it.
workInProgress.flags |= DidCapture;
// We should never render the children of a dehydrated boundary until we
// upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
return null;
}
if (state.dehydrated !== null) {
pushSuspenseContext(
workInProgress,
setDefaultShallowSuspenseContext(suspenseStackCursor.current),
);
// We know that this component will suspend again because if it has
// been unsuspended it has committed as a resolved Suspense component.
// If it needs to be retried, it should have work scheduled on it.
workInProgress.flags |= DidCapture;
// We should never render the children of a dehydrated boundary until we
// upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
return null;
}

// If this boundary is currently timed out, we need to decide
Expand Down
Loading

0 comments on commit 1f38d38

Please sign in to comment.