11'use client' ;
22
3+ /* eslint-disable no-underscore-dangle */
4+
35import * as React from 'react' ;
46import * as ReactDOMClient from 'react-dom/client' ;
57import { createFromReadableStream } from 'react-on-rails-rsc/client' ;
@@ -13,6 +15,12 @@ if (typeof use !== 'function') {
1315 throw new Error ( 'React.use is not defined. Please ensure you are using React 19 to use server components.' ) ;
1416}
1517
18+ declare global {
19+ interface Window {
20+ __FLIGHT_DATA : unknown [ ] ;
21+ }
22+ }
23+
1624export type RSCClientRootProps = {
1725 componentName : string ;
1826 rscPayloadGenerationUrlPath : string ;
@@ -35,6 +43,45 @@ const fetchRSC = ({ componentName, rscPayloadGenerationUrlPath, componentProps }
3543 return createFromFetch ( fetch ( `/${ strippedUrlPath } /${ componentName } ?props=${ propsString } ` ) ) ;
3644} ;
3745
46+ const createRSCStreamFromPage = ( ) => {
47+ let streamController : ReadableStreamController < unknown > | undefined ;
48+ const stream = new ReadableStream ( {
49+ start ( controller ) {
50+ if ( typeof window === 'undefined' ) {
51+ return ;
52+ }
53+ const handleChunk = ( chunk : unknown ) => {
54+ controller . enqueue ( chunk ) ;
55+ } ;
56+ if ( ! window . __FLIGHT_DATA ) {
57+ window . __FLIGHT_DATA = [ ] ;
58+ }
59+ window . __FLIGHT_DATA . forEach ( handleChunk ) ;
60+ window . __FLIGHT_DATA . push = ( ...chunks : unknown [ ] ) => {
61+ chunks . forEach ( handleChunk ) ;
62+ return chunks . length ;
63+ } ;
64+ streamController = controller ;
65+ }
66+ } ) ;
67+
68+ if ( typeof document !== 'undefined' && document . readyState === 'loading' ) {
69+ document . addEventListener ( 'DOMContentLoaded' , ( ) => {
70+ streamController ?. close ( ) ;
71+ } ) ;
72+ } else {
73+ streamController ?. close ( ) ;
74+ }
75+
76+ return stream ;
77+ }
78+
79+ const createFromRSCStream = ( ) => {
80+ const stream = createRSCStreamFromPage ( ) ;
81+ const transformedStream = transformRSCStreamAndReplayConsoleLogs ( stream ) ;
82+ return createFromReadableStream < React . ReactNode > ( transformedStream ) ;
83+ }
84+
3885/**
3986 * RSCClientRoot is a React component that handles client-side rendering of React Server Components (RSC).
4087 * It manages the fetching, caching, and rendering of RSC payloads from the server.
@@ -53,7 +100,6 @@ const RSCClientRoot: RenderFunction = async (
53100 _railsContext ?: RailsContext ,
54101 domNodeId ?: string ,
55102) => {
56- const root = await fetchRSC ( { componentName, rscPayloadGenerationUrlPath, componentProps } ) ;
57103 if ( ! domNodeId ) {
58104 throw new Error ( 'RSCClientRoot: No domNodeId provided' ) ;
59105 }
@@ -62,8 +108,10 @@ const RSCClientRoot: RenderFunction = async (
62108 throw new Error ( `RSCClientRoot: No DOM node found for id: ${ domNodeId } ` ) ;
63109 }
64110 if ( domNode . innerHTML ) {
111+ const root = await createFromRSCStream ( ) ;
65112 ReactDOMClient . hydrateRoot ( domNode , root ) ;
66113 } else {
114+ const root = await fetchRSC ( { componentName, rscPayloadGenerationUrlPath, componentProps } )
67115 ReactDOMClient . createRoot ( domNode ) . render ( root ) ;
68116 }
69117 // Added only to satisfy the return type of RenderFunction
0 commit comments