Skip to content

Commit a545a3c

Browse files
make the early hydration compatible with turbopack, backward compatible and refactor
1 parent 0d7bae6 commit a545a3c

File tree

6 files changed

+384
-325
lines changed

6 files changed

+384
-325
lines changed

lib/react_on_rails/helper.rb

Lines changed: 32 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ module Helper
1717
include ReactOnRails::Utils::Required
1818

1919
COMPONENT_HTML_KEY = "componentHtml"
20-
ADD_COMPONENT_TO_PENDING_HYDRATION_FUNCTION = "$ROR_PC"
21-
ADD_STORE_TO_PENDING_HYDRATION_FUNCTION = "$ROR_PS"
2220

2321
# react_component_name: can be a React function or class component or a "Render-Function".
2422
# "Render-Functions" differ from a React function in that they take two parameters, the
@@ -126,6 +124,7 @@ def react_component(component_name, options = {})
126124
# @option options [Boolean] :raise_on_prerender_error Set to true to raise exceptions during server-side rendering
127125
# Any other options are passed to the content tag, including the id.
128126
def stream_react_component(component_name, options = {})
127+
options = options.merge(force_load: true) unless options.key?(:force_load)
129128
run_stream_inside_fiber do
130129
internal_stream_react_component(component_name, options)
131130
end
@@ -195,9 +194,12 @@ def react_component_hash(component_name, options = {})
195194
# props: Ruby Hash or JSON string which contains the properties to pass to the redux store.
196195
# Options
197196
# defer: false -- pass as true if you wish to render this below your component.
198-
def redux_store(store_name, props: {}, defer: false)
197+
# force_load: false -- pass as true if you wish to hydrate this store immediately instead of
198+
# waiting for the page to load.
199+
def redux_store(store_name, props: {}, defer: false, force_load: false)
199200
redux_store_data = { store_name: store_name,
200-
props: props }
201+
props: props,
202+
force_load: force_load }
201203
if defer
202204
registered_stores_defer_render << redux_store_data
203205
"YOU SHOULD NOT SEE THIS ON YOUR VIEW -- Uses as a code block, like <% redux_store %> " \
@@ -463,7 +465,7 @@ def build_react_component_result_for_server_rendered_string(
463465

464466
result_console_script = render_options.replay_console ? console_script : ""
465467
result = compose_react_component_html_with_spec_and_console(
466-
component_specification_tag, rendered_output, result_console_script, render_options.dom_id
468+
component_specification_tag, rendered_output, result_console_script
467469
)
468470

469471
prepend_render_rails_context(result)
@@ -529,20 +531,13 @@ def build_react_component_result_for_server_rendered_hash(
529531
)
530532
end
531533

532-
def compose_react_component_html_with_spec_and_console(component_specification_tag, rendered_output, console_script, dom_id = nil)
533-
hydrate_script = if dom_id.present?
534-
add_component_to_pending_hydration_code = "window.#{ADD_COMPONENT_TO_PENDING_HYDRATION_FUNCTION}('#{dom_id}');"
535-
content_tag(:script, add_component_to_pending_hydration_code.html_safe)
536-
else
537-
""
538-
end
539-
534+
def compose_react_component_html_with_spec_and_console(component_specification_tag, rendered_output,
535+
console_script)
540536
# IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
541537
html_content = <<~HTML
542538
#{rendered_output}
543539
#{component_specification_tag}
544540
#{console_script}
545-
#{hydrate_script}
546541
HTML
547542
html_content.strip.html_safe
548543
end
@@ -554,30 +549,10 @@ def rails_context_if_not_already_rendered
554549

555550
@rendered_rails_context = true
556551

557-
rails_context_tag = content_tag(:script,
558-
json_safe_and_pretty(data).html_safe,
559-
type: "application/json",
560-
id: "js-react-on-rails-context")
561-
562-
pending_hydration_script = <<~JS.strip_heredoc
563-
window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS = [];
564-
window.REACT_ON_RAILS_PENDING_STORE_NAMES = [];
565-
window.#{ADD_COMPONENT_TO_PENDING_HYDRATION_FUNCTION} = function(domId) {
566-
window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS.push(domId);
567-
if (window.ReactOnRails) {
568-
window.ReactOnRails.renderOrHydrateLoadedComponents();
569-
}
570-
};
571-
window.#{ADD_STORE_TO_PENDING_HYDRATION_FUNCTION} = function(storeName) {
572-
window.REACT_ON_RAILS_PENDING_STORE_NAMES.push(storeName);
573-
if (window.ReactOnRails) {
574-
window.ReactOnRails.hydratePendingStores();
575-
}
576-
};
577-
JS
578-
rails_context_tag.concat(
579-
content_tag(:script, pending_hydration_script.html_safe)
580-
).html_safe
552+
content_tag(:script,
553+
json_safe_and_pretty(data).html_safe,
554+
type: "application/json",
555+
id: "js-react-on-rails-context")
581556
end
582557

583558
# prepend the rails_context if not yet applied
@@ -606,7 +581,7 @@ def internal_react_component(react_component_name, options = {})
606581
"data-trace" => (render_options.trace ? true : nil),
607582
"data-dom-id" => render_options.dom_id,
608583
"data-store-dependencies" => render_options.store_dependencies.to_json,
609-
)
584+
"data-force-load" => (render_options.force_load ? true : nil))
610585

611586
if render_options.force_load
612587
component_specification_tag.concat(
@@ -629,16 +604,21 @@ def internal_react_component(react_component_name, options = {})
629604

630605
def render_redux_store_data(redux_store_data)
631606
store_hydration_data = content_tag(:script,
632-
json_safe_and_pretty(redux_store_data[:props]).html_safe,
633-
type: "application/json",
634-
"data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe)
635-
hydration_code = "window.#{ADD_STORE_TO_PENDING_HYDRATION_FUNCTION}('#{redux_store_data[:store_name]}');"
636-
store_hydration_script = content_tag(:script, hydration_code.html_safe)
637-
638-
prepend_render_rails_context <<~HTML
639-
#{store_hydration_data}
640-
#{store_hydration_script}
641-
HTML
607+
json_safe_and_pretty(redux_store_data[:props]).html_safe,
608+
type: "application/json",
609+
"data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe,
610+
"data-force-load" => (redux_store_data[:force_load] ? true : nil))
611+
612+
if redux_store_data[:force_load]
613+
store_hydration_data.concat(
614+
content_tag(:script, <<~JS.strip_heredoc.html_safe
615+
ReactOnRails.reactOnRailsStoreLoaded('#{redux_store_data[:store_name]}');
616+
JS
617+
)
618+
)
619+
end
620+
621+
prepend_render_rails_context(store_hydration_data)
642622
end
643623

644624
def props_string(props)
@@ -738,7 +718,9 @@ def initialize_redux_stores(render_options)
738718
return result unless store_dependencies.present?
739719

740720
declarations = +"var reduxProps, store, storeGenerator;\n"
741-
store_objects = registered_stores_including_deferred.select { |store| store_dependencies.include?(store[:store_name]) }
721+
store_objects = registered_stores_including_deferred.select do |store|
722+
store_dependencies.include?(store[:store_name])
723+
end
742724

743725
result << store_objects.each_with_object(declarations) do |redux_store_data, memo|
744726
store_name = redux_store_data[:store_name]

0 commit comments

Comments
 (0)