Module: ReactOnRailsPro::ServerRenderingJsCode
- Defined in:
- lib/react_on_rails_pro/server_rendering_js_code.rb
Class Method Summary collapse
-
.async_props_setup_js(render_options) ⇒ String
Generates JavaScript code for async props setup when incremental rendering is enabled.
-
.generate_rsc_payload_js_function(render_options) ⇒ String
Generates the JavaScript function used for React Server Components payload generation Returns the JavaScript code that defines the generateRSCPayload function.
-
.render(props_string, rails_context, redux_stores, react_component_name, render_options) ⇒ String
Main rendering function that generates JavaScript code for server-side rendering.
- .ssr_pre_hook_js ⇒ Object
Class Method Details
.async_props_setup_js(render_options) ⇒ String
Generates JavaScript code for async props setup when incremental rendering is enabled.
This code runs DURING the initial render request, BEFORE the component renders. It sets up the infrastructure that allows:
-
Component to call ‘getReactOnRailsAsyncProp(“propName”)` → returns a Promise
-
Update chunks to call ‘asyncPropsManager.setProp(“propName”, value)` → resolves the Promise
WHY isRSCBundle CHECK?
-
Async props only work with React Server Components (RSC)
-
RSC bundle has ‘addAsyncPropsCapabilityToComponentProps` method
-
Server bundle (non-RSC) doesn’t support this pattern
RACE CONDITION HANDLING:
-
Uses getOrCreateAsyncPropsManager internally for lazy initialization
-
If initial render runs first: creates manager, stores in sharedExecutionContext
-
If update chunk arrives first: creates manager via getOrCreateAsyncPropsManager
-
Both share the same manager via sharedExecutionContext
WHY sharedExecutionContext?
-
The asyncPropManager needs to be accessible by update chunks that arrive later
-
Update chunks run in the same ExecutionContext, so they can retrieve it
-
sharedExecutionContext is NOT global - it’s scoped to this HTTP request
77 78 79 80 81 82 83 84 85 86 |
# File 'lib/react_on_rails_pro/server_rendering_js_code.rb', line 77 def async_props_setup_js() return "" unless .internal_option(:async_props_block) <<-JS if (ReactOnRails.isRSCBundle) { var { props: propsWithAsyncProps } = ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps, sharedExecutionContext); usedProps = propsWithAsyncProps; } JS end |
.generate_rsc_payload_js_function(render_options) ⇒ String
Generates the JavaScript function used for React Server Components payload generation Returns the JavaScript code that defines the generateRSCPayload function. It also adds necessary information to the railsContext to generate the RSC payload for any component in the app.
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/react_on_rails_pro/server_rendering_js_code.rb', line 14 def generate_rsc_payload_js_function() return "" unless ReactOnRailsPro.configuration.enable_rsc_support && .streaming? if .rsc_payload_streaming? # When already on RSC bundle, we prevent further RSC payload generation # by throwing an error if generateRSCPayload is called return <<-JS if (typeof generateRSCPayload !== 'function') { globalThis.generateRSCPayload = function generateRSCPayload() { throw new Error('The rendering request is already running on the RSC bundle. Please ensure that generateRSCPayload is only called from any React Server Component.') } } JS end # To minimize the size of the HTTP request body sent to the node renderer, # we reuse the existing rendering request string within the generateRSCPayload function. # This approach allows us to simply replace the component name and props, # rather than rewriting the entire rendering request. # This regex finds the empty function call pattern `()` and replaces it with the component and props <<-JS railsContext.serverSideRSCPayloadParameters = { renderingRequest, rscBundleHash: #{ReactOnRailsPro::Utils.rsc_bundle_hash.to_json}, } const runOnOtherBundle = globalThis.runOnOtherBundle; const generateRSCPayload = function generateRSCPayload(componentName, props, railsContext) { const { renderingRequest, rscBundleHash } = railsContext.serverSideRSCPayloadParameters; const propsString = JSON.stringify(props); const newRenderingRequest = renderingRequest.replace( /\\(\\s*\\)\\s*$/, function() { return `(${JSON.stringify(componentName)}, ${propsString})`; } ); return runOnOtherBundle(rscBundleHash, newRenderingRequest); } JS end |
.render(props_string, rails_context, redux_stores, react_component_name, render_options) ⇒ String
Main rendering function that generates JavaScript code for server-side rendering
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/react_on_rails_pro/server_rendering_js_code.rb', line 95 def render(props_string, rails_context, redux_stores, react_component_name, ) render_function_name = if ReactOnRailsPro.configuration.enable_rsc_support && .streaming? # Select appropriate function based on whether the rendering request is running on server or rsc bundle # As the same rendering request is used to generate the rsc payload and SSR the component. "ReactOnRails.isRSCBundle ? 'serverRenderRSCReactComponent' : 'streamServerRenderedReactComponent'" else "'serverRenderReactComponent'" end rsc_params = if ReactOnRailsPro.configuration.enable_rsc_support && .streaming? config = ReactOnRailsPro.configuration react_client_manifest_file = config.react_client_manifest_file react_server_client_manifest_file = config.react_server_client_manifest_file <<-JS railsContext.reactClientManifestFileName = #{react_client_manifest_file.to_json}; railsContext.reactServerClientManifestFileName = #{react_server_client_manifest_file.to_json}; JS else "" end # This function is called with specific componentName and props when generateRSCPayload is invoked # In that case, it replaces the empty () with ('componentName', props) in the rendering request <<-JS (function(componentName = #{react_component_name.to_json}, props = undefined) { var railsContext = #{rails_context}; #{rsc_params} #{generate_rsc_payload_js_function()} #{ssr_pre_hook_js} #{redux_stores} var usedProps = typeof props === 'undefined' ? #{props_string} : props; #{async_props_setup_js()} return ReactOnRails[#{render_function_name}]({ name: componentName, domNodeId: #{.dom_id.to_json}, props: usedProps, trace: #{.trace}, railsContext: railsContext, throwJsErrors: #{ReactOnRailsPro.configuration.throw_js_errors}, renderingReturnsPromises: #{ReactOnRailsPro.configuration.rendering_returns_promises}, generateRSCPayload: typeof generateRSCPayload !== 'undefined' ? generateRSCPayload : undefined, }); })() JS end |
.ssr_pre_hook_js ⇒ Object
6 7 8 |
# File 'lib/react_on_rails_pro/server_rendering_js_code.rb', line 6 def ssr_pre_hook_js ReactOnRailsPro.configuration.ssr_pre_hook_js || "" end |