Skip to content

Commit 49acb7b

Browse files
Add AsyncPropsManager JavaScript code generation
- Add async_props_setup_js helper method to generate AsyncPropsManager setup code - Generate code when async_props_block is present in render_options - Initialize AsyncPropsManager and store in sharedExecutionContext - Add tests for both async_props_setup_js and render method 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent f7d5c67 commit 49acb7b

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

react_on_rails_pro/lib/react_on_rails_pro/server_rendering_js_code.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,21 @@ def generate_rsc_payload_js_function(render_options)
4646
JS
4747
end
4848

49+
# Generates JavaScript code for async props setup when incremental rendering is enabled
50+
# @param render_options [Object] Options that control the rendering behavior
51+
# @return [String] JavaScript code that sets up AsyncPropsManager or empty string
52+
def async_props_setup_js(render_options)
53+
return "" unless render_options.internal_option(:async_props_block)
54+
55+
<<-JS
56+
if (ReactOnRails.isRSCBundle) {
57+
var { props: propsWithAsyncProps, asyncPropManager } = ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps);
58+
usedProps = propsWithAsyncProps;
59+
sharedExecutionContext.set("asyncPropsManager", asyncPropManager);
60+
}
61+
JS
62+
end
63+
4964
# Main rendering function that generates JavaScript code for server-side rendering
5065
# @param props_string [String] JSON string of props to pass to the React component
5166
# @param rails_context [String] JSON string of Rails context data
@@ -84,6 +99,7 @@ def render(props_string, rails_context, redux_stores, react_component_name, rend
8499
#{ssr_pre_hook_js}
85100
#{redux_stores}
86101
var usedProps = typeof props === 'undefined' ? #{props_string} : props;
102+
#{async_props_setup_js(render_options)}
87103
return ReactOnRails[#{render_function_name}]({
88104
name: componentName,
89105
domNodeId: '#{render_options.dom_id}',
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# frozen_string_literal: true
2+
3+
require_relative "spec_helper"
4+
require "react_on_rails_pro/server_rendering_js_code"
5+
6+
RSpec.describe ReactOnRailsPro::ServerRenderingJsCode do
7+
describe ".async_props_setup_js" do
8+
context "when async_props_block is NOT present in render_options" do
9+
let(:render_options) do
10+
instance_double(
11+
ReactOnRails::ReactComponent::RenderOptions,
12+
internal_option: nil
13+
)
14+
end
15+
16+
it "returns empty string" do
17+
result = described_class.async_props_setup_js(render_options)
18+
19+
expect(result).to eq("")
20+
end
21+
end
22+
23+
context "when async_props_block is present in render_options" do
24+
let(:async_props_block) { proc { { data: "async_data" } } }
25+
let(:render_options) do
26+
instance_double(
27+
ReactOnRails::ReactComponent::RenderOptions,
28+
internal_option: async_props_block
29+
)
30+
end
31+
32+
it "returns JavaScript code that sets up AsyncPropsManager" do
33+
result = described_class.async_props_setup_js(render_options)
34+
35+
expect(result).to include("ReactOnRails.isRSCBundle")
36+
expect(result).to include("ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps)")
37+
expect(result).to include("propsWithAsyncProps")
38+
expect(result).to include("asyncPropManager")
39+
expect(result).to include('sharedExecutionContext.set("asyncPropsManager", asyncPropManager)')
40+
expect(result).to include("usedProps = propsWithAsyncProps")
41+
end
42+
end
43+
end
44+
45+
describe ".render" do
46+
let(:props_string) { '{"name":"Test"}' }
47+
let(:rails_context) { '{"serverSide":true}' }
48+
let(:redux_stores) { "" }
49+
let(:react_component_name) { "TestComponent" }
50+
51+
context "when async_props_block is present" do
52+
let(:async_props_block) { proc { { data: "async_data" } } }
53+
let(:render_options) do
54+
instance_double(
55+
ReactOnRails::ReactComponent::RenderOptions,
56+
internal_option: async_props_block,
57+
streaming?: false,
58+
dom_id: "TestComponent-0",
59+
trace: false
60+
)
61+
end
62+
63+
before do
64+
allow(ReactOnRailsPro.configuration).to receive(:enable_rsc_support).and_return(false)
65+
allow(ReactOnRailsPro.configuration).to receive(:throw_js_errors).and_return(false)
66+
allow(ReactOnRailsPro.configuration).to receive(:rendering_returns_promises).and_return(false)
67+
allow(ReactOnRailsPro.configuration).to receive(:ssr_pre_hook_js).and_return(nil)
68+
end
69+
70+
it "includes async props setup JavaScript in the generated code" do
71+
result = described_class.render(
72+
props_string,
73+
rails_context,
74+
redux_stores,
75+
react_component_name,
76+
render_options
77+
)
78+
79+
expect(result).to include("var usedProps = typeof props === 'undefined' ?")
80+
expect(result).to include("ReactOnRails.isRSCBundle")
81+
expect(result).to include("ReactOnRails.addAsyncPropsCapabilityToComponentProps(usedProps)")
82+
expect(result).to include('sharedExecutionContext.set("asyncPropsManager", asyncPropManager)')
83+
end
84+
end
85+
86+
context "when async_props_block is NOT present" do
87+
let(:render_options) do
88+
instance_double(
89+
ReactOnRails::ReactComponent::RenderOptions,
90+
internal_option: nil,
91+
streaming?: false,
92+
dom_id: "TestComponent-0",
93+
trace: false
94+
)
95+
end
96+
97+
before do
98+
allow(ReactOnRailsPro.configuration).to receive(:enable_rsc_support).and_return(false)
99+
allow(ReactOnRailsPro.configuration).to receive(:throw_js_errors).and_return(false)
100+
allow(ReactOnRailsPro.configuration).to receive(:rendering_returns_promises).and_return(false)
101+
allow(ReactOnRailsPro.configuration).to receive(:ssr_pre_hook_js).and_return(nil)
102+
end
103+
104+
it "does NOT include async props setup JavaScript in the generated code" do
105+
result = described_class.render(
106+
props_string,
107+
rails_context,
108+
redux_stores,
109+
react_component_name,
110+
render_options
111+
)
112+
113+
expect(result).to include("var usedProps = typeof props === 'undefined' ?")
114+
expect(result).not_to include("ReactOnRails.addAsyncPropsCapabilityToComponentProps")
115+
expect(result).not_to include("asyncPropManager")
116+
end
117+
end
118+
end
119+
end

0 commit comments

Comments
 (0)