@@ -186,8 +186,7 @@ type ResumableNode =
186
186
| ResumableParentNode
187
187
| [
188
188
2 , // RESUME_SEGMENT
189
- string | null /* name */ ,
190
- string | number /* key */ ,
189
+ number /* index */ ,
191
190
number /* segment id */ ,
192
191
] ;
193
192
@@ -220,6 +219,7 @@ type SuspenseBoundary = {
220
219
221
220
export type Task = {
222
221
node : ReactNodeList ,
222
+ childIndex : number ,
223
223
ping : ( ) => void ,
224
224
blockedBoundary : Root | SuspenseBoundary ,
225
225
blockedSegment : Segment , // the segment we'll write to
@@ -1632,6 +1632,7 @@ function renderNodeDestructiveImpl(
1632
1632
// Stash the node we're working on. We'll pick up from this task in case
1633
1633
// something suspends.
1634
1634
task . node = node ;
1635
+ task . childIndex = childIndex ;
1635
1636
1636
1637
// Handle object types
1637
1638
if ( typeof node === 'object' && node !== null ) {
@@ -1809,18 +1810,45 @@ function renderChildrenArray(
1809
1810
for ( let i = 0 ; i < totalChildren ; i ++ ) {
1810
1811
const node = children [ i ] ;
1811
1812
task . treeContext = pushTreeContext ( prevTreeContext , totalChildren , i ) ;
1812
- if ( isArray ( node ) || getIteratorFn ( node ) ) {
1813
- // Nested arrays behave like a "fragment node" which is keyed.
1814
- // Therefore we need to add the current index as a parent key.
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 ) ) {
1815
1819
const prevKeyPath = task . keyPath ;
1816
1820
task . keyPath = [ task . keyPath , '' , childIndex ] ;
1817
- renderNode ( request , task , node , i ) ;
1821
+ renderChildrenArray ( request , task , node , i ) ;
1818
1822
task . keyPath = prevKeyPath ;
1819
- } else {
1820
- // We need to use the non-destructive form so that we can safely pop back
1821
- // up and render the sibling if something suspends.
1822
- renderNode ( request , task , node , i ) ;
1823
+ continue ;
1823
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
+
1849
+ // We need to use the non-destructive form so that we can safely pop back
1850
+ // up and render the sibling if something suspends.
1851
+ renderNode ( request , task , node , i ) ;
1824
1852
}
1825
1853
// Because this context is always set right before rendering every child, we
1826
1854
// only need to reset it to the previous value at the very end.
@@ -1831,6 +1859,7 @@ function trackPostpone(
1831
1859
request : Request ,
1832
1860
trackedPostpones : PostponedHoles ,
1833
1861
task : Task ,
1862
+ childIndex : number ,
1834
1863
segment : Segment ,
1835
1864
) : void {
1836
1865
segment . status = POSTPONED ;
@@ -1862,7 +1891,7 @@ function trackPostpone(
1862
1891
boundary . id ,
1863
1892
] ;
1864
1893
trackedPostpones . workingMap . set ( boundaryKeyPath , boundaryNode ) ;
1865
- addToResumableParent ( boundaryNode , boundaryKeyPath , trackedPostpones ) ;
1894
+ addToResumableParent ( boundaryNode , boundaryKeyPath [ 0 ] , trackedPostpones ) ;
1866
1895
}
1867
1896
1868
1897
const keyPath = task . keyPath ;
@@ -1872,12 +1901,7 @@ function trackPostpone(
1872
1901
) ;
1873
1902
}
1874
1903
1875
- const segmentNode : ResumableNode = [
1876
- RESUME_SEGMENT ,
1877
- keyPath [ 1 ] ,
1878
- keyPath [ 2 ] ,
1879
- segment . id ,
1880
- ] ;
1904
+ const segmentNode : ResumableNode = [ RESUME_SEGMENT , childIndex , segment . id ] ;
1881
1905
addToResumableParent ( segmentNode , keyPath , trackedPostpones ) ;
1882
1906
}
1883
1907
@@ -1941,6 +1965,7 @@ function spawnNewSuspendedTask(
1941
1965
task . context ,
1942
1966
task . treeContext ,
1943
1967
) ;
1968
+ newTask . childIndex = task . childIndex ;
1944
1969
1945
1970
if ( __DEV__ ) {
1946
1971
if ( task . componentStack !== null ) {
@@ -2035,7 +2060,13 @@ function renderNode(
2035
2060
task ,
2036
2061
postponeInstance . message ,
2037
2062
) ;
2038
- trackPostpone ( request , trackedPostpones , task , postponedSegment ) ;
2063
+ trackPostpone (
2064
+ request ,
2065
+ trackedPostpones ,
2066
+ task ,
2067
+ childIndex ,
2068
+ postponedSegment ,
2069
+ ) ;
2039
2070
2040
2071
// Restore the context. We assume that this will be restored by the inner
2041
2072
// functions in case nothing throws so we don't use "finally" here.
@@ -2328,7 +2359,13 @@ function retryTask(request: Request, task: Task): void {
2328
2359
const prevThenableState = task . thenableState ;
2329
2360
task . thenableState = null ;
2330
2361
2331
- renderNodeDestructive ( request , task , prevThenableState , task . node , 0 ) ;
2362
+ renderNodeDestructive (
2363
+ request ,
2364
+ task ,
2365
+ prevThenableState ,
2366
+ task . node ,
2367
+ task . childIndex ,
2368
+ ) ;
2332
2369
pushSegmentFinale (
2333
2370
segment . chunks ,
2334
2371
request . renderState ,
@@ -2377,8 +2414,15 @@ function retryTask(request: Request, task: Task): void {
2377
2414
task . abortSet . delete ( task ) ;
2378
2415
const postponeInstance : Postpone = ( x : any ) ;
2379
2416
logPostpone ( request , postponeInstance . message ) ;
2380
- trackPostpone ( request , trackedPostpones , task , segment ) ;
2417
+ trackPostpone (
2418
+ request ,
2419
+ trackedPostpones ,
2420
+ task ,
2421
+ task . childIndex ,
2422
+ segment ,
2423
+ ) ;
2381
2424
finishedTask ( request , task . blockedBoundary , segment ) ;
2425
+ return ;
2382
2426
}
2383
2427
}
2384
2428
task . abortSet . delete ( task ) ;
@@ -2975,10 +3019,9 @@ export function getResumableState(request: Request): ResumableState {
2975
3019
2976
3020
function addToResumableParent (
2977
3021
node : ResumableNode ,
2978
- keyPath : KeyNode ,
3022
+ parentKeyPath : Root | KeyNode ,
2979
3023
trackedPostpones : PostponedHoles ,
2980
3024
) : void {
2981
- const parentKeyPath = keyPath [ 0 ] ;
2982
3025
if ( parentKeyPath === null ) {
2983
3026
trackedPostpones . root . push ( node ) ;
2984
3027
} else {
@@ -2992,7 +3035,7 @@ function addToResumableParent(
2992
3035
( [ ] : Array < ResumableNode > ) ,
2993
3036
] : ResumableParentNode ) ;
2994
3037
workingMap . set ( parentKeyPath , parentNode ) ;
2995
- addToResumableParent ( parentNode , parentKeyPath , trackedPostpones ) ;
3038
+ addToResumableParent ( parentNode , parentKeyPath [ 0 ] , trackedPostpones ) ;
2996
3039
}
2997
3040
parentNode [ 3 ] . push ( node ) ;
2998
3041
}
0 commit comments