166
167
const REPLAY_NODE = 0;
168
const REPLAY_SUSPENSE_BOUNDARY = 1;
169
-const RESUME_SEGMENT = 2;
169
+const RESUME_ELEMENT = 2;
170
+const RESUME_SLOT = 3;
171
172
type ResumableParentNode =
173
| [
186
type ResumableNode =
187
| ResumableParentNode
188
| [
188
- 2, // RESUME_SEGMENT
189
+ 2, // RESUME_ELEMENT
190
+ string | null /* name */,
191
+ string | number /* key */,
192
+ number /* segment id */,
193
+ ]
194
+ | [
195
+ 3, // RESUME_SLOT
196
number /* index */,
197
number /* segment id */,
198
];
791
}
792
try {
793
// We use the safe form because we don't handle suspending here. Only error handling.
787
- renderNode(request, task, content, 0);
794
+ renderNode(request, task, content, -1);
795
pushSegmentFinale(
796
contentRootSegment.chunks,
797
request.renderState,
880
const segment = task.blockedSegment;
881
882
pushStartCompletedSuspenseBoundary(segment.chunks);
876
- renderNode(request, task, content, 0);
883
+ renderNode(request, task, content, -1);
884
pushEndCompletedSuspenseBoundary(segment.chunks);
885
886
popComponentStackInDEV(task);
910
911
// We use the non-destructive form because if something suspends, we still
912
// need to pop back up and finish this subtree of HTML.
906
- renderNode(request, task, children, 0);
913
+ renderNode(request, task, children, -1);
914
915
// We expect that errors will fatal the whole task and that we don't need
916
// the correct context. Therefore this is not in a finally.
977
childContextTypes,
978
);
979
task.legacyContext = mergedContext;
973
- renderNodeDestructive(request, task, null, nextChildren, 0);
980
+ renderNodeDestructive(request, task, null, nextChildren, -1);
981
task.legacyContext = previousContext;
982
return;
983
}
984
}
985
979
- renderNodeDestructive(request, task, null, nextChildren, 0);
986
+ renderNodeDestructive(request, task, null, nextChildren, -1);
987
}
988
989
function renderClassComponent(
1177
// Modify the id context. Because we'll need to reset this if something
1178
// suspends or errors, we'll use the non-destructive render path.
1179
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, index);
1173
- renderNode(request, task, children, 0);
1180
+ renderNode(request, task, children, -1);
1181
// Like the other contexts, this does not need to be in a finally block
1182
// because renderNode takes care of unwinding the stack.
1183
task.treeContext = prevTreeContext;
1185
// If there were formState hooks, we must use the non-destructive path
1186
// because this component is not a pure indirection; we emitted markers
1187
// to the stream.
1181
- renderNode(request, task, children, 0);
1188
+ renderNode(request, task, children, -1);
1189
} else {
1190
// We're now successfully past this task, and we haven't modified the
1191
// context stack. We don't have to pop back to the previous task every
1192
// again, so we can use the destructive recursive form.
1186
- renderNodeDestructive(request, task, null, children, 0);
1193
+ renderNodeDestructive(request, task, null, children, -1);
1194
}
1195
}
1196
1360
const newValue = readContext(context);
1361
const newChildren = render(newValue);
1362
1356
- renderNodeDestructive(request, task, null, newChildren, 0);
1363
+ renderNodeDestructive(request, task, null, newChildren, -1);
1364
}
1365
1366
function renderContextProvider(
1377
prevSnapshot = task.context;
1378
}
1379
task.context = pushProvider(context, value);
1373
- renderNodeDestructive(request, task, null, children, 0);
1380
+ renderNodeDestructive(request, task, null, children, -1);
1381
task.context = popProvider(context);
1382
if (__DEV__) {
1383
if (prevSnapshot !== task.context) {
1420
} else {
1421
// A visible Offscreen boundary is treated exactly like a fragment: a
1422
// pure indirection.
1416
- renderNodeDestructive(request, task, null, props.children, 0);
1423
+ renderNodeDestructive(request, task, null, props.children, -1);
1424
}
1425
}
1426
1467
case REACT_STRICT_MODE_TYPE:
1468
case REACT_PROFILER_TYPE:
1469
case REACT_FRAGMENT_TYPE: {
1463
- renderNodeDestructive(request, task, null, props.children, 0);
1470
+ renderNodeDestructive(request, task, null, props.children, -1);
1471
return;
1472
}
1473
case REACT_OFFSCREEN_TYPE: {
1477
case REACT_SUSPENSE_LIST_TYPE: {
1478
pushBuiltInComponentStackInDEV(task, 'SuspenseList');
1479
// TODO: SuspenseList should control the boundaries.
1473
- renderNodeDestructive(request, task, null, props.children, 0);
1480
+ renderNodeDestructive(request, task, null, props.children, -1);
1481
popComponentStackInDEV(task);
1482
return;
1483
}
1484
case REACT_SCOPE_TYPE: {
1485
if (enableScopeAPI) {
1479
- renderNodeDestructive(request, task, null, props.children, 0);
1486
+ renderNodeDestructive(request, task, null, props.children, -1);
1487
return;
1488
}
1489
throw new Error('ReactDOMServer does not yet support scope components.');
1652
const ref = element.ref;
1653
const name = getComponentNameFromType(type);
1654
const prevKeyPath = task.keyPath;
1648
- task.keyPath = [task.keyPath, name, key == null ? childIndex : key];
1655
+ task.keyPath = [
1656
+ task.keyPath,
1657
+ name,
1658
+ key == null ? (childIndex === -1 ? 0 : childIndex) : key,
1659
+ ];
1660
renderElement(request, task, prevThenableState, type, props, ref);
1661
task.keyPath = prevKeyPath;
1662
return;
1816
children: Array<any>,
1817
childIndex: number,
1818
) {
1819
+ const prevKeyPath = task.keyPath;
1820
+ if (childIndex !== -1) {
1821
+ task.keyPath = [task.keyPath, '', childIndex];
1822
+ }
1823
const prevTreeContext = task.treeContext;
1824
const totalChildren = children.length;
1825
for (let i = 0; i < totalChildren; i++) {
1826
const node = children[i];
1827
task.treeContext = pushTreeContext(prevTreeContext, totalChildren, i);
1813
-
1814
- // Nested arrays behave like a "fragment node" which is keyed.
1815
- // Therefore we need to add the current index as a parent key.
1816
- // We first check if the nested nodes are arrays or iterables.
1817
-
1818
- if (isArray(node)) {
1819
- const prevKeyPath = task.keyPath;
1820
- task.keyPath = [task.keyPath, '', childIndex];
1821
- renderChildrenArray(request, task, node, i);
1822
- task.keyPath = prevKeyPath;
1823
- continue;
1824
- }
1825
-
1826
- const iteratorFn = getIteratorFn(node);
1827
- if (iteratorFn) {
1828
- if (__DEV__) {
1829
- validateIterable(node, iteratorFn);
1830
- }
1831
- const iterator = iteratorFn.call(node);
1832
- if (iterator) {
1833
- let step = iterator.next();
1834
- if (!step.done) {
1835
- const prevKeyPath = task.keyPath;
1836
- task.keyPath = [task.keyPath, '', childIndex];
1837
- const nestedChildren = [];
1838
- do {
1839
- nestedChildren.push(step.value);
1840
- step = iterator.next();
1841
- } while (!step.done);
1842
- renderChildrenArray(request, task, nestedChildren, i);
1843
- task.keyPath = prevKeyPath;
1844
- }
1845
- continue;
1846
- }
1847
- }
1848
-
1828
// We need to use the non-destructive form so that we can safely pop back
1829
// up and render the sibling if something suspends.
1830
renderNode(request, task, node, i);
1832
// Because this context is always set right before rendering every child, we
1833
// only need to reset it to the previous value at the very end.
1834
task.treeContext = prevTreeContext;
1835
+ task.keyPath = prevKeyPath;
1836
}
1837
1838
function trackPostpone(
1839
request: Request,
1840
trackedPostpones: PostponedHoles,
1841
task: Task,
1862
- childIndex: number,
1842
segment: Segment,
1843
): void {
1844
segment.status = POSTPONED;
1880
);
1881
}
1882
1904
- const segmentNode: ResumableNode = [RESUME_SEGMENT, childIndex, segment.id];
1905
- addToResumableParent(segmentNode, keyPath, trackedPostpones);
1883
+ if (task.childIndex === -1) {
1884
+ // Resume at the position before the first array
1885
+ const resumableElement = [
1886
+ RESUME_ELEMENT,
1887
+ keyPath[1],
1888
+ keyPath[2],
1889
+ segment.id,
1890
+ ];
1891
+ addToResumableParent(resumableElement, keyPath[0], trackedPostpones);
1892
+ } else {
1893
+ // Resume at the slot within the array
1894
+ const resumableNode = [RESUME_SLOT, task.childIndex, segment.id];
1895
+ addToResumableParent(resumableNode, keyPath, trackedPostpones);
1896
+ }
1897
}
1898
1899
function injectPostponedHole(
2051
task,
2052
postponeInstance.message,
2053
);
2063
- trackPostpone(
2064
- request,
2065
- trackedPostpones,
2066
- task,
2067
- childIndex,
2068
- postponedSegment,
2069
- );
2054
+ trackPostpone(request, trackedPostpones, task, postponedSegment);
2055
2056
// Restore the context. We assume that this will be restored by the inner
2057
// functions in case nothing throws so we don't use "finally" here.
2399
task.abortSet.delete(task);
2400
const postponeInstance: Postpone = (x: any);
2401
logPostpone(request, postponeInstance.message);
2417
- trackPostpone(
2418
- request,
2419
- trackedPostpones,
2420
- task,
2421
- task.childIndex,
2422
- segment,
2423
- );
2402
+ trackPostpone(request, trackedPostpones, task, segment);
2403
finishedTask(request, task.blockedBoundary, segment);
2404
return;
2405
}