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