@@ -4,7 +4,7 @@ import type { ReactElement } from 'react';
44
55import ComponentRegistry from './ComponentRegistry' ;
66import createReactOutput from './createReactOutput' ;
7- import { isPromise , isServerRenderHash } from './isServerRenderResult' ;
7+ import { isServerRenderHash } from './isServerRenderResult' ;
88import buildConsoleReplay from './buildConsoleReplay' ;
99import handleError from './handleError' ;
1010import { createResultObject , convertToError , validateComponent } from './serverRenderUtils' ;
@@ -128,7 +128,10 @@ export const transformRenderStreamChunksToResultObject = (renderState: StreamRen
128128 return { readableStream, pipeToTransform, writeChunk, emitError, endStream } ;
129129} ;
130130
131- const streamRenderReactComponent = ( reactRenderingResult : ReactElement , options : RenderParams ) => {
131+ const streamRenderReactComponent = (
132+ reactRenderingResult : ReactElement | Promise < ReactElement | string > ,
133+ options : RenderParams ,
134+ ) => {
132135 const { name : componentName , throwJsErrors, domNodeId } = options ;
133136 const renderState : StreamRenderState = {
134137 result : null ,
@@ -139,42 +142,63 @@ const streamRenderReactComponent = (reactRenderingResult: ReactElement, options:
139142 const { readableStream, pipeToTransform, writeChunk, emitError, endStream } =
140143 transformRenderStreamChunksToResultObject ( renderState ) ;
141144
142- const renderingStream = ReactDOMServer . renderToPipeableStream ( reactRenderingResult , {
143- onShellError ( e ) {
144- const error = convertToError ( e ) ;
145- renderState . hasErrors = true ;
146- renderState . error = error ;
145+ const onShellError = ( e : unknown ) => {
146+ const error = convertToError ( e ) ;
147+ renderState . hasErrors = true ;
148+ renderState . error = error ;
147149
148- if ( throwJsErrors ) {
149- emitError ( error ) ;
150- }
150+ if ( throwJsErrors ) {
151+ emitError ( error ) ;
152+ }
151153
152- const errorHtml = handleError ( { e : error , name : componentName , serverSide : true } ) ;
153- writeChunk ( errorHtml ) ;
154- endStream ( ) ;
155- } ,
156- onShellReady ( ) {
157- renderState . isShellReady = true ;
158- pipeToTransform ( renderingStream ) ;
159- } ,
160- onError ( e ) {
161- if ( ! renderState . isShellReady ) {
154+ const errorHtml = handleError ( { e : error , name : componentName , serverSide : true } ) ;
155+ writeChunk ( errorHtml ) ;
156+ endStream ( ) ;
157+ } ;
158+
159+ Promise . resolve ( reactRenderingResult )
160+ . then ( ( reactRenderedElement ) => {
161+ if ( typeof reactRenderedElement === 'string' ) {
162+ console . error (
163+ `Error: stream_react_component helper received a string instead of a React component for component "${ componentName } ".\n` +
164+ 'To benefit from React on Rails Pro streaming feature, your render function should return a React component.\n' +
165+ 'Do not call ReactDOMServer.renderToString() inside the render function as this defeats the purpose of streaming.\n' ,
166+ ) ;
167+
168+ writeChunk ( reactRenderedElement ) ;
169+ endStream ( ) ;
162170 return ;
163171 }
164- const error = convertToError ( e ) ;
165- if ( throwJsErrors ) {
166- emitError ( error ) ;
167- }
168- renderState . hasErrors = true ;
169- renderState . error = error ;
170- } ,
171- identifierPrefix : domNodeId ,
172- } ) ;
172+
173+ const renderingStream = ReactDOMServer . renderToPipeableStream ( reactRenderedElement , {
174+ onShellError,
175+ onShellReady ( ) {
176+ renderState . isShellReady = true ;
177+ pipeToTransform ( renderingStream ) ;
178+ } ,
179+ onError ( e ) {
180+ if ( ! renderState . isShellReady ) {
181+ return ;
182+ }
183+ const error = convertToError ( e ) ;
184+ if ( throwJsErrors ) {
185+ emitError ( error ) ;
186+ }
187+ renderState . hasErrors = true ;
188+ renderState . error = error ;
189+ } ,
190+ identifierPrefix : domNodeId ,
191+ } ) ;
192+ } )
193+ . catch ( onShellError ) ;
173194
174195 return readableStream ;
175196} ;
176197
177- type StreamRenderer < T , P extends RenderParams > = ( reactElement : ReactElement , options : P ) => T ;
198+ type StreamRenderer < T , P extends RenderParams > = (
199+ reactElement : ReactElement | Promise < ReactElement | string > ,
200+ options : P ,
201+ ) => T ;
178202
179203export const streamServerRenderedComponent = < T , P extends RenderParams > (
180204 options : P ,
@@ -194,7 +218,7 @@ export const streamServerRenderedComponent = <T, P extends RenderParams>(
194218 railsContext,
195219 } ) ;
196220
197- if ( isServerRenderHash ( reactRenderingResult ) || isPromise ( reactRenderingResult ) ) {
221+ if ( isServerRenderHash ( reactRenderingResult ) ) {
198222 throw new Error ( 'Server rendering of streams is not supported for server render hashes or promises.' ) ;
199223 }
200224
0 commit comments