@@ -15,7 +15,7 @@ declare global {
1515
1616const mapRailsContextToRSCPayloadStreams = new Map < string , RSCPayloadStreamInfo [ ] > ( ) ;
1717
18- const rscPayloadCallbacks = new Map < string , Array < RSCPayloadCallback > > ( ) ;
18+ const rscPayloadCallbacks = new Map < string , RSCPayloadCallback [ ] > ( ) ;
1919
2020/**
2121 * Registers a callback to be executed when RSC payloads are generated.
@@ -36,13 +36,18 @@ export const onRSCPayloadGenerated = (
3636 callback : RSCPayloadCallback ,
3737) => {
3838 const { renderRequestId } = railsContext . componentSpecificMetadata ;
39- const callbacks = rscPayloadCallbacks . get ( renderRequestId ) || [ ] ;
40- callbacks . push ( callback ) ;
41- rscPayloadCallbacks . set ( renderRequestId , callbacks ) ;
39+ const callbacks = rscPayloadCallbacks . get ( renderRequestId ) ;
40+ if ( callbacks ) {
41+ callbacks . push ( callback ) ;
42+ } else {
43+ rscPayloadCallbacks . set ( renderRequestId , [ callback ] ) ;
44+ }
4245
4346 // Call callback for any existing streams for this context
44- const existingStreams = mapRailsContextToRSCPayloadStreams . get ( renderRequestId ) || [ ] ;
45- existingStreams . forEach ( ( streamInfo ) => callback ( streamInfo ) ) ;
47+ const existingStreams = mapRailsContextToRSCPayloadStreams . get ( renderRequestId ) ;
48+ if ( existingStreams ) {
49+ existingStreams . forEach ( ( streamInfo ) => callback ( streamInfo ) ) ;
50+ }
4651} ;
4752
4853/**
@@ -76,7 +81,10 @@ export const getRSCPayloadStream = async (
7681
7782 const { renderRequestId } = railsContext . componentSpecificMetadata ;
7883 const stream = await generateRSCPayload ( componentName , props , railsContext ) ;
79- const streams = mapRailsContextToRSCPayloadStreams . get ( renderRequestId ) ?? [ ] ;
84+ // Tee stream to allow for multiple consumers:
85+ // 1. stream1 - Used by React's runtime to perform server-side rendering
86+ // 2. stream2 - Used by react-on-rails to embed the RSC payloads
87+ // into the HTML stream for client-side hydration
8088 const stream1 = new PassThrough ( ) ;
8189 stream . pipe ( stream1 ) ;
8290 const stream2 = new PassThrough ( ) ;
@@ -87,13 +95,19 @@ export const getRSCPayloadStream = async (
8795 props,
8896 stream : stream2 ,
8997 } ;
90- streams . push ( streamInfo ) ;
91- mapRailsContextToRSCPayloadStreams . set ( renderRequestId , streams ) ;
98+ const streams = mapRailsContextToRSCPayloadStreams . get ( renderRequestId ) ;
99+ if ( streams ) {
100+ streams . push ( streamInfo ) ;
101+ } else {
102+ mapRailsContextToRSCPayloadStreams . set ( renderRequestId , [ streamInfo ] ) ;
103+ }
92104
93105 // Notify callbacks about the new stream in a sync manner to maintain proper hydration timing
94106 // as described in the comment above onRSCPayloadGenerated
95- const callbacks = rscPayloadCallbacks . get ( renderRequestId ) || [ ] ;
96- callbacks . forEach ( ( callback ) => callback ( streamInfo ) ) ;
107+ const callbacks = rscPayloadCallbacks . get ( renderRequestId ) ;
108+ if ( callbacks ) {
109+ callbacks . forEach ( ( callback ) => callback ( streamInfo ) ) ;
110+ }
97111
98112 return stream1 ;
99113} ;
0 commit comments