Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions packages/react-dom/src/__tests__/ReactDOMFizzSuspenseList-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ describe('ReactDOMFizzSuspenseList', () => {
}

// @gate enableSuspenseList
it('shows content independently by default', async () => {
it('shows content forwards by default', async () => {
const A = createAsyncText('A');
const B = createAsyncText('B');
const C = createAsyncText('C');
Expand All @@ -157,31 +157,38 @@ describe('ReactDOMFizzSuspenseList', () => {
);
}

await A.resolve();
await C.resolve();

await serverAct(async () => {
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(<Foo />);
pipe(writable);
});

assertLog(['A', 'Suspend! [B]', 'Suspend! [C]', 'Loading B', 'Loading C']);
assertLog([
'Suspend! [A]',
'Suspend! [B]', // TODO: Defer rendering the content after fallback if previous suspended,
'C',
'Loading A',
'Loading B',
'Loading C',
]);

expect(getVisibleChildren(container)).toEqual(
<div>
<span>A</span>
<span>Loading A</span>
<span>Loading B</span>
<span>Loading C</span>
</div>,
);

await serverAct(() => C.resolve());
assertLog(['C']);
await serverAct(() => A.resolve());
assertLog(['A']);

expect(getVisibleChildren(container)).toEqual(
<div>
<span>A</span>
<span>Loading B</span>
<span>C</span>
<span>Loading C</span>
</div>,
);

Expand Down
3 changes: 2 additions & 1 deletion packages/react-reconciler/src/ReactChildFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -2104,7 +2104,8 @@ export function validateSuspenseListChildren(
) {
if (__DEV__) {
if (
(revealOrder === 'forwards' ||
(revealOrder == null ||
revealOrder === 'forwards' ||
revealOrder === 'backwards' ||
revealOrder === 'unstable_legacy-backwards') &&
children !== undefined &&
Expand Down
80 changes: 34 additions & 46 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -3245,20 +3245,15 @@ function validateRevealOrder(revealOrder: SuspenseListRevealOrder) {
if (__DEV__) {
const cacheKey = revealOrder == null ? 'null' : revealOrder;
if (
revealOrder != null &&
revealOrder !== 'forwards' &&
revealOrder !== 'unstable_legacy-backwards' &&
revealOrder !== 'together' &&
revealOrder !== 'independent' &&
!didWarnAboutRevealOrder[cacheKey]
) {
didWarnAboutRevealOrder[cacheKey] = true;
if (revealOrder == null) {
console.error(
'The default for the <SuspenseList revealOrder="..."> prop is changing. ' +
'To be future compatible you must explictly specify either ' +
'"independent" (the current default), "together", "forwards" or "legacy_unstable-backwards".',
);
} else if (revealOrder === 'backwards') {
if (revealOrder === 'backwards') {
console.error(
'The rendering order of <SuspenseList revealOrder="backwards"> is changing. ' +
'To be future compatible you must specify revealOrder="legacy_unstable-backwards" instead.',
Expand Down Expand Up @@ -3314,18 +3309,7 @@ function validateTailOptions(
const cacheKey = tailMode == null ? 'null' : tailMode;
if (!didWarnAboutTailOptions[cacheKey]) {
if (tailMode == null) {
if (
revealOrder === 'forwards' ||
revealOrder === 'backwards' ||
revealOrder === 'unstable_legacy-backwards'
) {
didWarnAboutTailOptions[cacheKey] = true;
console.error(
'The default for the <SuspenseList tail="..."> prop is changing. ' +
'To be future compatible you must explictly specify either ' +
'"visible" (the current default), "collapsed" or "hidden".',
);
}
// The default tail is now "hidden".
} else if (
tailMode !== 'visible' &&
tailMode !== 'collapsed' &&
Expand All @@ -3338,14 +3322,15 @@ function validateTailOptions(
tailMode,
);
} else if (
revealOrder != null &&
revealOrder !== 'forwards' &&
revealOrder !== 'backwards' &&
revealOrder !== 'unstable_legacy-backwards'
) {
didWarnAboutTailOptions[cacheKey] = true;
console.error(
'<SuspenseList tail="%s" /> is only valid if revealOrder is ' +
'"forwards" or "backwards". ' +
'"forwards" (default) or "backwards". ' +
'Did you mean to specify revealOrder="forwards"?',
tailMode,
);
Expand Down Expand Up @@ -3449,30 +3434,6 @@ function updateSuspenseListComponent(
workInProgress.memoizedState = null;
} else {
switch (revealOrder) {
case 'forwards': {
const lastContentRow = findLastContentRow(workInProgress.child);
let tail;
if (lastContentRow === null) {
// The whole list is part of the tail.
// TODO: We could fast path by just rendering the tail now.
tail = workInProgress.child;
workInProgress.child = null;
} else {
// Disconnect the tail rows after the content row.
// We're going to render them separately later.
tail = lastContentRow.sibling;
lastContentRow.sibling = null;
}
initSuspenseListRenderState(
workInProgress,
false, // isBackwards
tail,
lastContentRow,
tailMode,
treeForkCount,
);
break;
}
case 'backwards':
case 'unstable_legacy-backwards': {
// We're going to find the first row that has existing content.
Expand Down Expand Up @@ -3517,10 +3478,37 @@ function updateSuspenseListComponent(
);
break;
}
default: {
// The default reveal order is the same as not having
case 'independent': {
// The "independent" reveal order is the same as not having
// a boundary.
workInProgress.memoizedState = null;
break;
}
// The default is now forwards.
case 'forwards':
default: {
const lastContentRow = findLastContentRow(workInProgress.child);
let tail;
if (lastContentRow === null) {
// The whole list is part of the tail.
// TODO: We could fast path by just rendering the tail now.
tail = workInProgress.child;
workInProgress.child = null;
} else {
// Disconnect the tail rows after the content row.
// We're going to render them separately later.
tail = lastContentRow.sibling;
lastContentRow.sibling = null;
}
initSuspenseListRenderState(
workInProgress,
false, // isBackwards
tail,
lastContentRow,
tailMode,
treeForkCount,
);
break;
}
}
}
Expand Down
29 changes: 18 additions & 11 deletions packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,11 @@ function cutOffTailIfNeeded(
return;
}
switch (renderState.tailMode) {
case 'hidden': {
case 'visible': {
// Everything should remain as it was.
break;
}
case 'collapsed': {
// Any insertions at the end of the tail list after this point
// should be invisible. If there are already mounted boundaries
// anything before them are not considered for collapsing.
Expand All @@ -716,15 +720,23 @@ function cutOffTailIfNeeded(
// last rendered item.
if (lastTailNode === null) {
// All remaining items in the tail are insertions.
renderState.tail = null;
if (!hasRenderedATailFallback && renderState.tail !== null) {
// We suspended during the head. We want to show at least one
// row at the tail. So we'll keep on and cut off the rest.
renderState.tail.sibling = null;
} else {
renderState.tail = null;
}
} else {
// Detach the insertion after the last node that was already
// inserted.
lastTailNode.sibling = null;
}
break;
}
case 'collapsed': {
// Hidden is now the default.
case 'hidden':
default: {
// Any insertions at the end of the tail list after this point
// should be invisible. If there are already mounted boundaries
// anything before them are not considered for collapsing.
Expand All @@ -742,13 +754,7 @@ function cutOffTailIfNeeded(
// last rendered item.
if (lastTailNode === null) {
// All remaining items in the tail are insertions.
if (!hasRenderedATailFallback && renderState.tail !== null) {
// We suspended during the head. We want to show at least one
// row at the tail. So we'll keep on and cut off the rest.
renderState.tail.sibling = null;
} else {
renderState.tail = null;
}
renderState.tail = null;
} else {
// Detach the insertion after the last node that was already
// inserted.
Expand Down Expand Up @@ -1795,7 +1801,8 @@ function completeWork(
// This might have been modified.
if (
renderState.tail === null &&
renderState.tailMode === 'hidden' &&
renderState.tailMode !== 'collapsed' &&
renderState.tailMode !== 'visible' &&
!renderedTail.alternate &&
!getIsHydrating() // We don't cut it if we're hydrating.
) {
Expand Down
5 changes: 1 addition & 4 deletions packages/react-reconciler/src/ReactFiberSuspenseComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,7 @@ export function findFirstSuspended(row: Fiber): null | Fiber {
node.tag === SuspenseListComponent &&
// Independent revealOrder can't be trusted because it doesn't
// keep track of whether it suspended or not.
(node.memoizedProps.revealOrder === 'forwards' ||
node.memoizedProps.revealOrder === 'backwards' ||
node.memoizedProps.revealOrder === 'unstable_legacy-backwards' ||
node.memoizedProps.revealOrder === 'together')
node.memoizedProps.revealOrder !== 'independent'
) {
const didSuspend = (node.flags & DidCapture) !== NoFlags;
if (didSuspend) {
Expand Down
Loading
Loading