@@ -17,6 +17,8 @@ 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"
2022
2123 # react_component_name: can be a React function or class component or a "Render-Function".
2224 # "Render-Functions" differ from a React function in that they take two parameters, the
@@ -400,6 +402,17 @@ def run_stream_inside_fiber
400402 rendering_fiber . resume
401403 end
402404
405+ def registered_stores
406+ ( @registered_stores || [ ] ) + ( @registered_stores_defer_render || [ ] )
407+ end
408+
409+ def create_render_options ( react_component_name , options )
410+ # If no store dependencies are passed, default to all registered stores up till now
411+ options [ :store_dependencies ] ||= registered_stores . map { |store | store [ :store_name ] }
412+ ReactOnRails ::ReactComponent ::RenderOptions . new ( react_component_name : react_component_name ,
413+ options : options )
414+ end
415+
403416 def internal_stream_react_component ( component_name , options = { } )
404417 options = options . merge ( stream? : true )
405418 result = internal_react_component ( component_name , options )
@@ -511,12 +524,8 @@ def build_react_component_result_for_server_rendered_hash(
511524 end
512525
513526 def compose_react_component_html_with_spec_and_console ( component_specification_tag , rendered_output , console_script , dom_id = nil )
514- hydrate_script = dom_id . present? ? content_tag ( :script , %(
515- window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS.push('#{ dom_id } ');
516- if (window.ReactOnRails) {
517- window.ReactOnRails.renderOrHydrateLoadedComponents();
518- }
519- ) . html_safe ) : ""
527+ add_component_to_pending_hydration_code = "window.#{ ADD_COMPONENT_TO_PENDING_HYDRATION_FUNCTION } ('#{ dom_id } ');"
528+ hydrate_script = dom_id . present? ? content_tag ( :script , add_component_to_pending_hydration_code . html_safe ) : ""
520529 # IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
521530 html_content = <<~HTML
522531 #{ rendered_output }
@@ -538,11 +547,26 @@ def rails_context_if_not_already_rendered
538547 json_safe_and_pretty ( data ) . html_safe ,
539548 type : "application/json" ,
540549 id : "js-react-on-rails-context" )
550+
551+ pending_hydration_script = <<~JS . strip_heredoc
552+ window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS = [];
553+ window.REACT_ON_RAILS_PENDING_STORE_NAMES = [];
554+ window.#{ ADD_COMPONENT_TO_PENDING_HYDRATION_FUNCTION } = function(domId) {
555+ window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS.push(domId);
556+ if (window.ReactOnRails) {
557+ window.ReactOnRails.renderOrHydrateLoadedComponents();
558+ }
559+ };
560+ window.#{ ADD_STORE_TO_PENDING_HYDRATION_FUNCTION } = function(storeName) {
561+ window.REACT_ON_RAILS_PENDING_STORE_NAMES.push(storeName);
562+ if (window.ReactOnRails) {
563+ window.ReactOnRails.hydratePendingStores();
564+ }
565+ };
566+ JS
541567 rails_context_tag . concat (
542- content_tag ( :script , %(
543- window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS = [];
544- ) . html_safe )
545- )
568+ content_tag ( :script , pending_hydration_script . html_safe )
569+ ) . html_safe
546570 end
547571
548572 # prepend the rails_context if not yet applied
@@ -558,8 +582,7 @@ def internal_react_component(react_component_name, options = {})
558582 # (re-hydrate the data). This enables react rendered on the client to see that the
559583 # server has already rendered the HTML.
560584
561- render_options = ReactOnRails ::ReactComponent ::RenderOptions . new ( react_component_name : react_component_name ,
562- options : options )
585+ render_options = create_render_options ( react_component_name , options )
563586
564587 # Setup the page_loaded_js, which is the same regardless of prerendering or not!
565588 # The reason is that React is smart about not doing extra work if the server rendering did its job.
@@ -570,7 +593,9 @@ def internal_react_component(react_component_name, options = {})
570593 id : "js-react-on-rails-component-#{ render_options . dom_id } " ,
571594 "data-component-name" => render_options . react_component_name ,
572595 "data-trace" => ( render_options . trace ? true : nil ) ,
573- "data-dom-id" => render_options . dom_id )
596+ "data-dom-id" => render_options . dom_id ,
597+ "data-store-dependencies" => render_options . store_dependencies . to_json ,
598+ )
574599
575600 if render_options . force_load
576601 component_specification_tag . concat (
@@ -592,12 +617,17 @@ def internal_react_component(react_component_name, options = {})
592617 end
593618
594619 def render_redux_store_data ( redux_store_data )
595- result = content_tag ( :script ,
620+ store_hydration_data = content_tag ( :script ,
596621 json_safe_and_pretty ( redux_store_data [ :props ] ) . html_safe ,
597622 type : "application/json" ,
598623 "data-js-react-on-rails-store" => redux_store_data [ :store_name ] . html_safe )
624+ hydration_code = "window.#{ ADD_STORE_TO_PENDING_HYDRATION_FUNCTION } ('#{ redux_store_data [ :store_name ] } ');"
625+ store_hydration_script = content_tag ( :script , hydration_code . html_safe )
599626
600- prepend_render_rails_context ( result )
627+ prepend_render_rails_context <<~HTML
628+ #{ store_hydration_data }
629+ #{ store_hydration_script }
630+ HTML
601631 end
602632
603633 def props_string ( props )
@@ -654,7 +684,7 @@ def server_rendered_react_component(render_options)
654684 js_code = ReactOnRails ::ServerRenderingJsCode . server_rendering_component_js_code (
655685 props_string : props_string ( props ) . gsub ( "\u2028 " , '\u2028' ) . gsub ( "\u2029 " , '\u2029' ) ,
656686 rails_context : rails_context ( server_side : true ) . to_json ,
657- redux_stores : initialize_redux_stores ,
687+ redux_stores : initialize_redux_stores ( render_options ) ,
658688 react_component_name : react_component_name ,
659689 render_options : render_options
660690 )
@@ -688,17 +718,18 @@ def server_rendered_react_component(render_options)
688718 result
689719 end
690720
691- def initialize_redux_stores
721+ def initialize_redux_stores ( render_options )
692722 result = +<<-JS
693723 ReactOnRails.clearHydratedStores();
694724 JS
695725
696- return result unless @registered_stores . present? || @registered_stores_defer_render . present?
726+ store_dependencies = render_options . store_dependencies
727+ return result unless store_dependencies . present?
697728
698729 declarations = +"var reduxProps, store, storeGenerator;\n "
699- all_stores = ( @ registered_stores || [ ] ) + ( @registered_stores_defer_render || [ ] )
730+ store_objects = registered_stores . select { | store | store_dependencies . include? ( store [ :store_name ] ) }
700731
701- result << all_stores . each_with_object ( declarations ) do |redux_store_data , memo |
732+ result << store_objects . each_with_object ( declarations ) do |redux_store_data , memo |
702733 store_name = redux_store_data [ :store_name ]
703734 props = props_string ( redux_store_data [ :props ] )
704735 memo << <<-JS . strip_heredoc
0 commit comments