Skip to content

Commit

Permalink
Add tail="collapsed" option
Browse files Browse the repository at this point in the history
  • Loading branch information
sebmarkbage committed Jun 27, 2019
1 parent 46a3ede commit 0bbef6b
Show file tree
Hide file tree
Showing 4 changed files with 589 additions and 0 deletions.
39 changes: 39 additions & 0 deletions packages/react-reconciler/src/ReactFiberBeginWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {
SuspenseState,
SuspenseListRenderState,
SuspenseListTailOptions,
} from './ReactFiberSuspenseComponent';
import type {SuspenseContext} from './ReactFiberSuspenseContext';

Expand Down Expand Up @@ -191,6 +192,7 @@ let didWarnAboutFunctionRefs;
export let didWarnAboutReassigningProps;
let didWarnAboutMaxDuration;
let didWarnAboutRevealOrder;
let didWarnAboutTailOptions;

if (__DEV__) {
didWarnAboutBadClass = {};
Expand All @@ -201,6 +203,7 @@ if (__DEV__) {
didWarnAboutReassigningProps = false;
didWarnAboutMaxDuration = false;
didWarnAboutRevealOrder = {};
didWarnAboutTailOptions = {};
}

export function reconcileChildren(
Expand Down Expand Up @@ -2066,11 +2069,40 @@ function validateRevealOrder(revealOrder: SuspenseListRevealOrder) {
}
}

function validateTailOptions(
tailOptions: SuspenseListTailOptions,
revealOrder: SuspenseListRevealOrder,
) {
if (__DEV__) {
if (tailOptions !== undefined && !didWarnAboutTailOptions[tailOptions]) {
if (tailOptions !== 'collapsed') {
didWarnAboutTailOptions[tailOptions] = true;
warning(
false,
'"%s" is not a supported value for tail on <SuspenseList />. ' +
'Did you mean "collapsed"?',
tailOptions,
);
} else if (revealOrder !== 'forwards' && revealOrder !== 'backwards') {
didWarnAboutTailOptions[tailOptions] = true;
warning(
false,
'<SuspenseList tail="%s" /> is only valid if revealOrder is ' +
'"forwards" or "backwards". ' +
'Did you mean to specify revealOrder="forwards"?',
tailOptions,
);
}
}
}
}

function initSuspenseListRenderState(
workInProgress: Fiber,
isBackwards: boolean,
tail: null | Fiber,
lastContentRow: null | Fiber,
tailOptions: SuspenseListTailOptions,
): void {
let renderState: null | SuspenseListRenderState =
workInProgress.memoizedState;
Expand All @@ -2081,6 +2113,7 @@ function initSuspenseListRenderState(
last: lastContentRow,
tail: tail,
tailExpiration: 0,
tailOptions: tailOptions,
};
} else {
// We can reuse the existing object from previous renders.
Expand All @@ -2089,6 +2122,7 @@ function initSuspenseListRenderState(
renderState.last = lastContentRow;
renderState.tail = tail;
renderState.tailExpiration = 0;
renderState.tailOptions = tailOptions;
}
}

Expand All @@ -2106,9 +2140,11 @@ function updateSuspenseListComponent(
) {
const nextProps = workInProgress.pendingProps;
const revealOrder: SuspenseListRevealOrder = nextProps.revealOrder;
const tailOptions: SuspenseListTailOptions = nextProps.tail;
const newChildren = nextProps.children;

validateRevealOrder(revealOrder);
validateTailOptions(tailOptions, revealOrder);

function printChildren(child) {
if (!child) {
Expand Down Expand Up @@ -2175,6 +2211,7 @@ function updateSuspenseListComponent(
false, // isBackwards
tail,
lastContentRow,
tailOptions,
);
break;
}
Expand Down Expand Up @@ -2205,6 +2242,7 @@ function updateSuspenseListComponent(
true, // isBackwards
tail,
null, // last
tailOptions,
);
break;
}
Expand All @@ -2214,6 +2252,7 @@ function updateSuspenseListComponent(
false, // isBackwards
null, // tail
null, // last
undefined,
);
break;
}
Expand Down
47 changes: 47 additions & 0 deletions packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,46 @@ if (supportsMutation) {
};
}

function cutOffTailIfNeeded(renderState: SuspenseListRenderState) {
switch (renderState.tailOptions) {
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.
// Therefore we need to go through the whole tail to find if
// there are any.
let tailNode = renderState.tail;
let lastTailNode = null;
while (tailNode !== null) {
if (tailNode.alternate !== null) {
lastTailNode = tailNode;
}
tailNode = tailNode.sibling;
}
// Next we're simply going to delete all insertions after the
// last rendered item.
if (lastTailNode === null) {
// All remaining items in the tail are insertions.
if (renderState.rendering === null && 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;
}
default: {
// TODO: warn if not undefined
}
}
}

// Note this, might mutate the workInProgress passed in.
function hasSuspendedChildrenAndNewContent(
workInProgress: Fiber,
Expand Down Expand Up @@ -993,6 +1033,9 @@ function completeWork(
didSuspendAlready =
(workInProgress.effectTag & DidCapture) !== NoEffect;
}
if (didSuspendAlready) {
cutOffTailIfNeeded(renderState);
}
// Next we're going to render the tail.
} else {
// Append the rendered row to the child list.
Expand All @@ -1006,6 +1049,9 @@ function completeWork(
// The assumption is that this is usually faster.
workInProgress.effectTag |= DidCapture;
didSuspendAlready = true;

cutOffTailIfNeeded(renderState);

// Since nothing actually suspended, there will nothing to ping this
// to get it started back up to attempt the next item. If we can show
// them, then they really have the same priority as this render.
Expand All @@ -1020,6 +1066,7 @@ function completeWork(
} else if (isShowingAnyFallbacks(renderedTail)) {
workInProgress.effectTag |= DidCapture;
didSuspendAlready = true;
cutOffTailIfNeeded(renderState);
}
}
if (renderState.isBackwards) {
Expand Down
4 changes: 4 additions & 0 deletions packages/react-reconciler/src/ReactFiberSuspenseComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {SuspenseComponent} from 'shared/ReactWorkTags';
// Alternatively we can make this use an effect tag similar to SuspenseList.
export type SuspenseState = {||};

export type SuspenseListTailOptions = 'collapsed' | void;

export type SuspenseListRenderState = {|
isBackwards: boolean,
// The currently rendering tail row.
Expand All @@ -24,6 +26,8 @@ export type SuspenseListRenderState = {|
tail: null | Fiber,
// The absolute time in ms that we'll expire the tail rendering.
tailExpiration: number,
// Tail insertions setting.
tailOptions: SuspenseListTailOptions,
|};

export function shouldCaptureSuspense(
Expand Down
Loading

0 comments on commit 0bbef6b

Please sign in to comment.