@@ -2300,4 +2300,189 @@ describe('ReactSuspenseList', () => {
2300
2300
// remount.
2301
2301
expect ( previousInst ) . toBe ( setAsyncB ) ;
2302
2302
} ) ;
2303
+
2304
+ it ( 'is able to re-suspend the last rows during an update with hidden' , async ( ) => {
2305
+ let AsyncB = createAsyncText ( 'B' ) ;
2306
+
2307
+ let setAsyncB ;
2308
+
2309
+ function B ( ) {
2310
+ let [ shouldBeAsync , setAsync ] = React . useState ( false ) ;
2311
+ setAsyncB = setAsync ;
2312
+
2313
+ return shouldBeAsync ? (
2314
+ < Suspense fallback = { < Text text = "Loading B" /> } >
2315
+ < AsyncB />
2316
+ </ Suspense >
2317
+ ) : (
2318
+ < Text text = "Sync B" />
2319
+ ) ;
2320
+ }
2321
+
2322
+ function Foo ( { updateList} ) {
2323
+ return (
2324
+ < SuspenseList revealOrder = "forwards" tail = "hidden" >
2325
+ < Suspense key = "A" fallback = { < Text text = "Loading A" /> } >
2326
+ < Text text = "A" />
2327
+ </ Suspense >
2328
+ < B key = "B" updateList = { updateList } />
2329
+ </ SuspenseList >
2330
+ ) ;
2331
+ }
2332
+
2333
+ ReactNoop . render ( < Foo /> ) ;
2334
+
2335
+ expect ( Scheduler ) . toFlushAndYield ( [ 'A' , 'Sync B' ] ) ;
2336
+
2337
+ expect ( ReactNoop ) . toMatchRenderedOutput (
2338
+ < >
2339
+ < span > A</ span >
2340
+ < span > Sync B</ span >
2341
+ </ > ,
2342
+ ) ;
2343
+
2344
+ let previousInst = setAsyncB ;
2345
+
2346
+ // During an update we suspend on B.
2347
+ ReactNoop . act ( ( ) => setAsyncB ( true ) ) ;
2348
+
2349
+ expect ( Scheduler ) . toHaveYielded ( [
2350
+ 'Suspend! [B]' ,
2351
+ 'Loading B' ,
2352
+ // The second pass is the "force hide" pass
2353
+ 'Loading B' ,
2354
+ ] ) ;
2355
+
2356
+ expect ( ReactNoop ) . toMatchRenderedOutput (
2357
+ < >
2358
+ < span > A</ span >
2359
+ < span > Loading B</ span >
2360
+ </ > ,
2361
+ ) ;
2362
+
2363
+ // Before we resolve we'll rerender the whole list.
2364
+ // This should leave the tree intact.
2365
+ ReactNoop . act ( ( ) => ReactNoop . render ( < Foo updateList = { true } /> ) ) ;
2366
+
2367
+ expect ( Scheduler ) . toHaveYielded ( [ 'A' , 'Suspend! [B]' , 'Loading B' ] ) ;
2368
+
2369
+ expect ( ReactNoop ) . toMatchRenderedOutput (
2370
+ < >
2371
+ < span > A</ span >
2372
+ < span > Loading B</ span >
2373
+ </ > ,
2374
+ ) ;
2375
+
2376
+ await AsyncB . resolve ( ) ;
2377
+
2378
+ expect ( Scheduler ) . toFlushAndYield ( [ 'B' ] ) ;
2379
+
2380
+ expect ( ReactNoop ) . toMatchRenderedOutput (
2381
+ < >
2382
+ < span > A</ span >
2383
+ < span > B</ span >
2384
+ </ > ,
2385
+ ) ;
2386
+
2387
+ // This should be the same instance. I.e. it didn't
2388
+ // remount.
2389
+ expect ( previousInst ) . toBe ( setAsyncB ) ;
2390
+ } ) ;
2391
+
2392
+ it ( 'is able to interrupt a partially rendered tree and continue later' , async ( ) => {
2393
+ let AsyncA = createAsyncText ( 'A' ) ;
2394
+
2395
+ let updateLowPri ;
2396
+ let updateHighPri ;
2397
+
2398
+ function Bar ( ) {
2399
+ let [ highPriState , setHighPriState ] = React . useState ( false ) ;
2400
+ updateHighPri = setHighPriState ;
2401
+ return highPriState ? < AsyncA /> : null ;
2402
+ }
2403
+
2404
+ function Foo ( ) {
2405
+ let [ lowPriState , setLowPriState ] = React . useState ( false ) ;
2406
+ updateLowPri = setLowPriState ;
2407
+ return (
2408
+ < SuspenseList revealOrder = "forwards" tail = "hidden" >
2409
+ < Suspense key = "A" fallback = { < Text text = "Loading A" /> } >
2410
+ < Bar />
2411
+ </ Suspense >
2412
+ { lowPriState ? < Text text = "B" /> : null }
2413
+ { lowPriState ? < Text text = "C" /> : null }
2414
+ { lowPriState ? < Text text = "D" /> : null }
2415
+ </ SuspenseList >
2416
+ ) ;
2417
+ }
2418
+
2419
+ ReactNoop . render ( < Foo /> ) ;
2420
+
2421
+ expect ( Scheduler ) . toFlushAndYield ( [ ] ) ;
2422
+
2423
+ expect ( ReactNoop ) . toMatchRenderedOutput ( null ) ;
2424
+
2425
+ ReactNoop . act ( ( ) => {
2426
+ // Add a few items at the end.
2427
+ updateLowPri ( true ) ;
2428
+
2429
+ // Flush partially through.
2430
+ expect ( Scheduler ) . toFlushAndYieldThrough ( [ 'B' , 'C' ] ) ;
2431
+
2432
+ // Schedule another update at higher priority.
2433
+ Scheduler . unstable_runWithPriority (
2434
+ Scheduler . unstable_UserBlockingPriority ,
2435
+ ( ) => updateHighPri ( true ) ,
2436
+ ) ;
2437
+
2438
+ // That will intercept the previous render.
2439
+ } ) ;
2440
+
2441
+ jest . runAllTimers ( ) ;
2442
+
2443
+ expect ( Scheduler ) . toHaveYielded (
2444
+ __DEV__
2445
+ ? [
2446
+ // First attempt at high pri.
2447
+ 'Suspend! [A]' ,
2448
+ 'Loading A' ,
2449
+ // Re-render at forced.
2450
+ 'Suspend! [A]' ,
2451
+ 'Loading A' ,
2452
+ // We auto-commit this on DEV.
2453
+ // Try again on low-pri.
2454
+ 'Suspend! [A]' ,
2455
+ 'Loading A' ,
2456
+ ]
2457
+ : [
2458
+ // First attempt at high pri.
2459
+ 'Suspend! [A]' ,
2460
+ 'Loading A' ,
2461
+ // Re-render at forced.
2462
+ 'Suspend! [A]' ,
2463
+ 'Loading A' ,
2464
+ // We didn't commit so retry at low-pri.
2465
+ 'Suspend! [A]' ,
2466
+ 'Loading A' ,
2467
+ // Re-render at forced.
2468
+ 'Suspend! [A]' ,
2469
+ 'Loading A' ,
2470
+ ] ,
2471
+ ) ;
2472
+
2473
+ expect ( ReactNoop ) . toMatchRenderedOutput ( < span > Loading A</ span > ) ;
2474
+
2475
+ await AsyncA . resolve ( ) ;
2476
+
2477
+ expect ( Scheduler ) . toFlushAndYield ( [ 'A' , 'B' , 'C' , 'D' ] ) ;
2478
+
2479
+ expect ( ReactNoop ) . toMatchRenderedOutput (
2480
+ < >
2481
+ < span > A</ span >
2482
+ < span > B</ span >
2483
+ < span > C</ span >
2484
+ < span > D</ span >
2485
+ </ > ,
2486
+ ) ;
2487
+ } ) ;
2303
2488
} ) ;
0 commit comments