@@ -438,4 +438,92 @@ describe('ReactFlightDOM', () => {
438438 '<p>Game over</p>' , // TODO: should not have message in prod.
439439 ) ;
440440 } ) ;
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+ } ) ;
441529} ) ;
0 commit comments