@@ -440,4 +440,92 @@ describe('ReactFlightDOM', () => {
440
440
'<p>Game over</p>' , // TODO: should not have message in prod.
441
441
) ;
442
442
} ) ;
443
+
444
+ // @gate experimental
445
+ it ( 'should preserve state of client components on refetch' , async ( ) => {
446
+ const { Suspense} = React ;
447
+
448
+ // Client
449
+
450
+ function Page ( { response} ) {
451
+ return response . readRoot ( ) ;
452
+ }
453
+
454
+ function Input ( ) {
455
+ return < input /> ;
456
+ }
457
+
458
+ const InputClient = moduleReference ( Input ) ;
459
+
460
+ // Server
461
+
462
+ function App ( { color} ) {
463
+ // Verify both DOM and Client children.
464
+ return (
465
+ < div style = { { color} } >
466
+ < input />
467
+ < InputClient />
468
+ </ div >
469
+ ) ;
470
+ }
471
+
472
+ const container = document . createElement ( 'div' ) ;
473
+ const root = ReactDOM . unstable_createRoot ( container ) ;
474
+
475
+ const stream1 = getTestStream ( ) ;
476
+ ReactTransportDOMServer . pipeToNodeWritable (
477
+ < App color = "red" /> ,
478
+ stream1 . writable ,
479
+ webpackMap ,
480
+ ) ;
481
+ const response1 = ReactTransportDOMClient . createFromReadableStream (
482
+ stream1 . readable ,
483
+ ) ;
484
+ await act ( async ( ) => {
485
+ root . render (
486
+ < Suspense fallback = { < p > (loading)</ p > } >
487
+ < Page response = { response1 } />
488
+ </ Suspense > ,
489
+ ) ;
490
+ } ) ;
491
+ expect ( container . children . length ) . toBe ( 1 ) ;
492
+ expect ( container . children [ 0 ] . tagName ) . toBe ( 'DIV' ) ;
493
+ expect ( container . children [ 0 ] . style . color ) . toBe ( 'red' ) ;
494
+
495
+ // Change the DOM state for both inputs.
496
+ const inputA = container . children [ 0 ] . children [ 0 ] ;
497
+ expect ( inputA . tagName ) . toBe ( 'INPUT' ) ;
498
+ inputA . value = 'hello' ;
499
+ const inputB = container . children [ 0 ] . children [ 1 ] ;
500
+ expect ( inputB . tagName ) . toBe ( 'INPUT' ) ;
501
+ inputB . value = 'goodbye' ;
502
+
503
+ const stream2 = getTestStream ( ) ;
504
+ ReactTransportDOMServer . pipeToNodeWritable (
505
+ < App color = "blue" /> ,
506
+ stream2 . writable ,
507
+ webpackMap ,
508
+ ) ;
509
+ const response2 = ReactTransportDOMClient . createFromReadableStream (
510
+ stream2 . readable ,
511
+ ) ;
512
+ await act ( async ( ) => {
513
+ root . render (
514
+ < Suspense fallback = { < p > (loading)</ p > } >
515
+ < Page response = { response2 } />
516
+ </ Suspense > ,
517
+ ) ;
518
+ } ) ;
519
+ expect ( container . children . length ) . toBe ( 1 ) ;
520
+ expect ( container . children [ 0 ] . tagName ) . toBe ( 'DIV' ) ;
521
+ expect ( container . children [ 0 ] . style . color ) . toBe ( 'blue' ) ;
522
+
523
+ // Verify we didn't destroy the DOM for either input.
524
+ expect ( inputA === container . children [ 0 ] . children [ 0 ] ) . toBe ( true ) ;
525
+ expect ( inputA . tagName ) . toBe ( 'INPUT' ) ;
526
+ expect ( inputA . value ) . toBe ( 'hello' ) ;
527
+ expect ( inputB === container . children [ 0 ] . children [ 1 ] ) . toBe ( true ) ;
528
+ expect ( inputB . tagName ) . toBe ( 'INPUT' ) ;
529
+ expect ( inputB . value ) . toBe ( 'goodbye' ) ;
530
+ } ) ;
443
531
} ) ;
0 commit comments