@@ -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
@@ -362,13 +364,13 @@ def load_pack_for_generated_component(react_component_name, render_options)
362364
363365 ReactOnRails ::PackerUtils . raise_nested_entries_disabled unless ReactOnRails ::PackerUtils . nested_entries?
364366 append_javascript_pack_tag ( "client-bundle" )
365- # if Rails.env.development?
366- # is_component_pack_present = File.exist?(generated_components_pack_path(react_component_name))
367- # raise_missing_autoloaded_bundle(react_component_name) unless is_component_pack_present
368- # end
369- # append_javascript_pack_tag("generated/#{react_component_name}",
370- # defer: ReactOnRails.configuration.defer_generated_component_packs)
371- # append_stylesheet_pack_tag("generated/#{react_component_name}")
367+ if Rails . env . development?
368+ is_component_pack_present = File . exist? ( generated_components_pack_path ( react_component_name ) )
369+ raise_missing_autoloaded_bundle ( react_component_name ) unless is_component_pack_present
370+ end
371+ append_javascript_pack_tag ( "generated/#{ react_component_name } " ,
372+ defer : ReactOnRails . configuration . defer_generated_component_packs )
373+ append_stylesheet_pack_tag ( "generated/#{ react_component_name } " )
372374 end
373375
374376 # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
@@ -401,6 +403,17 @@ def run_stream_inside_fiber
401403 rendering_fiber . resume
402404 end
403405
406+ def registered_stores
407+ ( @registered_stores || [ ] ) + ( @registered_stores_defer_render || [ ] )
408+ end
409+
410+ def create_render_options ( react_component_name , options )
411+ # If no store dependencies are passed, default to all registered stores up till now
412+ options [ :store_dependencies ] ||= registered_stores . map { |store | store [ :store_name ] }
413+ ReactOnRails ::ReactComponent ::RenderOptions . new ( react_component_name : react_component_name ,
414+ options : options )
415+ end
416+
404417 def internal_stream_react_component ( component_name , options = { } )
405418 options = options . merge ( stream? : true )
406419 result = internal_react_component ( component_name , options )
@@ -512,12 +525,8 @@ def build_react_component_result_for_server_rendered_hash(
512525 end
513526
514527 def compose_react_component_html_with_spec_and_console ( component_specification_tag , rendered_output , console_script , dom_id = nil )
515- hydrate_script = dom_id . present? ? content_tag ( :script , %(
516- window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS.push('#{ dom_id } ');
517- if (window.ReactOnRails) {
518- window.ReactOnRails.renderOrHydrateLoadedComponents();
519- }
520- ) . html_safe ) : ""
528+ add_component_to_pending_hydration_code = "window.#{ ADD_COMPONENT_TO_PENDING_HYDRATION_FUNCTION } ('#{ dom_id } ');"
529+ hydrate_script = dom_id . present? ? content_tag ( :script , add_component_to_pending_hydration_code . html_safe ) : ""
521530 # IMPORTANT: Ensure that we mark string as html_safe to avoid escaping.
522531 html_content = <<~HTML
523532 #{ rendered_output }
@@ -539,11 +548,26 @@ def rails_context_if_not_already_rendered
539548 json_safe_and_pretty ( data ) . html_safe ,
540549 type : "application/json" ,
541550 id : "js-react-on-rails-context" )
551+
552+ pending_hydration_script = <<~JS . strip_heredoc
553+ window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS = [];
554+ window.REACT_ON_RAILS_PENDING_STORE_NAMES = [];
555+ window.#{ ADD_COMPONENT_TO_PENDING_HYDRATION_FUNCTION } = function(domId) {
556+ window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS.push(domId);
557+ if (window.ReactOnRails) {
558+ window.ReactOnRails.renderOrHydrateLoadedComponents();
559+ }
560+ };
561+ window.#{ ADD_STORE_TO_PENDING_HYDRATION_FUNCTION } = function(storeName) {
562+ window.REACT_ON_RAILS_PENDING_STORE_NAMES.push(storeName);
563+ if (window.ReactOnRails) {
564+ window.ReactOnRails.hydratePendingStores();
565+ }
566+ };
567+ JS
542568 rails_context_tag . concat (
543- content_tag ( :script , %(
544- window.REACT_ON_RAILS_PENDING_COMPONENT_DOM_IDS = [];
545- ) . html_safe )
546- )
569+ content_tag ( :script , pending_hydration_script . html_safe )
570+ ) . html_safe
547571 end
548572
549573 # prepend the rails_context if not yet applied
@@ -559,8 +583,7 @@ def internal_react_component(react_component_name, options = {})
559583 # (re-hydrate the data). This enables react rendered on the client to see that the
560584 # server has already rendered the HTML.
561585
562- render_options = ReactOnRails ::ReactComponent ::RenderOptions . new ( react_component_name : react_component_name ,
563- options : options )
586+ render_options = create_render_options ( react_component_name , options )
564587
565588 # Setup the page_loaded_js, which is the same regardless of prerendering or not!
566589 # The reason is that React is smart about not doing extra work if the server rendering did its job.
@@ -571,7 +594,9 @@ def internal_react_component(react_component_name, options = {})
571594 id : "js-react-on-rails-component-#{ render_options . dom_id } " ,
572595 "data-component-name" => render_options . react_component_name ,
573596 "data-trace" => ( render_options . trace ? true : nil ) ,
574- "data-dom-id" => render_options . dom_id )
597+ "data-dom-id" => render_options . dom_id ,
598+ "data-store-dependencies" => render_options . store_dependencies . to_json ,
599+ )
575600
576601 if render_options . force_load
577602 component_specification_tag . concat (
@@ -593,12 +618,17 @@ def internal_react_component(react_component_name, options = {})
593618 end
594619
595620 def render_redux_store_data ( redux_store_data )
596- result = content_tag ( :script ,
621+ store_hydration_data = content_tag ( :script ,
597622 json_safe_and_pretty ( redux_store_data [ :props ] ) . html_safe ,
598623 type : "application/json" ,
599624 "data-js-react-on-rails-store" => redux_store_data [ :store_name ] . html_safe )
625+ hydration_code = "window.#{ ADD_STORE_TO_PENDING_HYDRATION_FUNCTION } ('#{ redux_store_data [ :store_name ] } ');"
626+ store_hydration_script = content_tag ( :script , hydration_code . html_safe )
600627
601- prepend_render_rails_context ( result )
628+ prepend_render_rails_context <<~HTML
629+ #{ store_hydration_data }
630+ #{ store_hydration_script }
631+ HTML
602632 end
603633
604634 def props_string ( props )
@@ -655,7 +685,7 @@ def server_rendered_react_component(render_options)
655685 js_code = ReactOnRails ::ServerRenderingJsCode . server_rendering_component_js_code (
656686 props_string : props_string ( props ) . gsub ( "\u2028 " , '\u2028' ) . gsub ( "\u2029 " , '\u2029' ) ,
657687 rails_context : rails_context ( server_side : true ) . to_json ,
658- redux_stores : initialize_redux_stores ,
688+ redux_stores : initialize_redux_stores ( render_options ) ,
659689 react_component_name : react_component_name ,
660690 render_options : render_options
661691 )
@@ -689,17 +719,18 @@ def server_rendered_react_component(render_options)
689719 result
690720 end
691721
692- def initialize_redux_stores
722+ def initialize_redux_stores ( render_options )
693723 result = +<<-JS
694724 ReactOnRails.clearHydratedStores();
695725 JS
696726
697- return result unless @registered_stores . present? || @registered_stores_defer_render . present?
727+ store_dependencies = render_options . store_dependencies
728+ return result unless store_dependencies . present?
698729
699730 declarations = +"var reduxProps, store, storeGenerator;\n "
700- all_stores = ( @ registered_stores || [ ] ) + ( @registered_stores_defer_render || [ ] )
731+ store_objects = registered_stores . select { | store | store_dependencies . include? ( store [ :store_name ] ) }
701732
702- result << all_stores . each_with_object ( declarations ) do |redux_store_data , memo |
733+ result << store_objects . each_with_object ( declarations ) do |redux_store_data , memo |
703734 store_name = redux_store_data [ :store_name ]
704735 props = props_string ( redux_store_data [ :props ] )
705736 memo << <<-JS . strip_heredoc
0 commit comments