diff --git a/lib/react_on_rails/configuration.rb b/lib/react_on_rails/configuration.rb index 76c69e5c87..b624da46f8 100644 --- a/lib/react_on_rails/configuration.rb +++ b/lib/react_on_rails/configuration.rb @@ -154,9 +154,9 @@ def check_component_registry_timeout raise ReactOnRails::Error, "component_registry_timeout must be a positive integer" end - # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + # rubocop:disable Metrics/CyclomaticComplexity def validate_generated_component_packs_loading_strategy - # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + # rubocop:enable Metrics/CyclomaticComplexity if defer_generated_component_packs if %i[async sync].include?(generated_component_packs_loading_strategy) @@ -176,12 +176,11 @@ def validate_generated_component_packs_loading_strategy 1. Use :sync or :defer loading strategy instead of :async 2. Upgrade to Shakapacker v8.2.0 or above to enable async script loading MSG - if PackerUtils.supports_async_loading? - self.generated_component_packs_loading_strategy ||= :async - elsif generated_component_packs_loading_strategy.nil? - Rails.logger.warn("**WARNING** #{msg}") - self.generated_component_packs_loading_strategy = :sync - elsif generated_component_packs_loading_strategy == :async + if generated_component_packs_loading_strategy.nil? + # Use defer as the default to ensure generated component packs load and register + # components before main bundle executes, avoiding race conditions with async loading + self.generated_component_packs_loading_strategy = :defer + elsif generated_component_packs_loading_strategy == :async && !PackerUtils.supports_async_loading? raise ReactOnRails::Error, "**ERROR** #{msg}" end diff --git a/spec/dummy/app/views/layouts/application.html.erb b/spec/dummy/app/views/layouts/application.html.erb index 0e25174d52..8c7bc06bb0 100644 --- a/spec/dummy/app/views/layouts/application.html.erb +++ b/spec/dummy/app/views/layouts/application.html.erb @@ -9,16 +9,10 @@ <%= yield :head %> - <%# Conditionally use defer: true for pages with Redux shared stores (inline registration). - Modern apps should use async: true for optimal performance. See docs for details: - docs/building-features/streaming-server-rendering.md %> - <% if uses_redux_shared_store? %> - <%# defer: true required for Redux shared stores with inline component registration %> - <%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', defer: true) %> - <% else %> - <%# async: true is the recommended approach for modern apps (Shakapacker >= 8.2.0) %> - <%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', async: true) %> - <% end %> + <%# Use defer: true to ensure proper script execution order. + When using generated component packs (auto_load_bundle), defer ensures + component registrations complete before React hydration begins. %> + <%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', defer: true) %> <%= csrf_meta_tags %> diff --git a/spec/dummy/config/initializers/react_on_rails.rb b/spec/dummy/config/initializers/react_on_rails.rb index 54c2f40d5c..7578a995df 100644 --- a/spec/dummy/config/initializers/react_on_rails.rb +++ b/spec/dummy/config/initializers/react_on_rails.rb @@ -42,5 +42,6 @@ def self.adjust_props_for_client_side_hydration(component_name, props) config.components_subdirectory = "startup" config.auto_load_bundle = true config.immediate_hydration = false - config.generated_component_packs_loading_strategy = :defer + # Don't explicitly set generated_component_packs_loading_strategy - let it default to :defer + # which ensures generated component packs load and register components before main bundle executes end diff --git a/spec/react_on_rails/configuration_spec.rb b/spec/react_on_rails/configuration_spec.rb index e3123fcdb0..e4198b8d94 100644 --- a/spec/react_on_rails/configuration_spec.rb +++ b/spec/react_on_rails/configuration_spec.rb @@ -284,9 +284,9 @@ module ReactOnRails .with("8.2.0").and_return(true) end - it "defaults to :async" do + it "defaults to :defer" do ReactOnRails.configure {} # rubocop:disable Lint/EmptyBlock - expect(ReactOnRails.configuration.generated_component_packs_loading_strategy).to eq(:async) + expect(ReactOnRails.configuration.generated_component_packs_loading_strategy).to eq(:defer) end it "accepts :async value" do @@ -332,10 +332,9 @@ module ReactOnRails allow(Rails.logger).to receive(:warn) end - it "defaults to :sync and logs a warning" do + it "defaults to :defer" do ReactOnRails.configure {} # rubocop:disable Lint/EmptyBlock - expect(ReactOnRails.configuration.generated_component_packs_loading_strategy).to eq(:sync) - expect(Rails.logger).to have_received(:warn).with(/does not support async script loading/) + expect(ReactOnRails.configuration.generated_component_packs_loading_strategy).to eq(:defer) end it "accepts :defer value" do