Skip to content

Commit 5abe219

Browse files
committed
Fix component registration race condition by using defer for all scripts
Changes the default loading strategy from :async to :defer to prevent race conditions where React hydration starts before component registrations complete. Root cause: With async script loading, both client-bundle and generated component packs can execute in any order. When client-bundle executes first, React attempts to hydrate components that haven't been registered yet, causing "Could not find component registered with name X" errors. Solution: - Default generated_component_packs_loading_strategy to :defer (was :async) - Use defer: true for client-bundle in dummy app layout - Update comments to reflect new strategy With defer, scripts execute in DOM order after parsing, ensuring: 1. Generated component packs load first 2. Components register before main bundle executes 3. React hydration finds all registered components Fixes CI failures in: - spec/system/integration_spec.rb[1:1:6:1:2] - spec/system/integration_spec.rb[1:1:6:2:2]
1 parent 9d2710a commit 5abe219

File tree

3 files changed

+13
-19
lines changed

3 files changed

+13
-19
lines changed

lib/react_on_rails/configuration.rb

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ def check_component_registry_timeout
154154
raise ReactOnRails::Error, "component_registry_timeout must be a positive integer"
155155
end
156156

157-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
157+
# rubocop:disable Metrics/CyclomaticComplexity
158158
def validate_generated_component_packs_loading_strategy
159-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
159+
# rubocop:enable Metrics/CyclomaticComplexity
160160

161161
if defer_generated_component_packs
162162
if %i[async sync].include?(generated_component_packs_loading_strategy)
@@ -176,12 +176,11 @@ def validate_generated_component_packs_loading_strategy
176176
1. Use :sync or :defer loading strategy instead of :async
177177
2. Upgrade to Shakapacker v8.2.0 or above to enable async script loading
178178
MSG
179-
if PackerUtils.supports_async_loading?
180-
self.generated_component_packs_loading_strategy ||= :async
181-
elsif generated_component_packs_loading_strategy.nil?
182-
Rails.logger.warn("**WARNING** #{msg}")
183-
self.generated_component_packs_loading_strategy = :sync
184-
elsif generated_component_packs_loading_strategy == :async
179+
if generated_component_packs_loading_strategy.nil?
180+
# Use defer as the default to ensure generated component packs load and register
181+
# components before main bundle executes, avoiding race conditions with async loading
182+
self.generated_component_packs_loading_strategy = :defer
183+
elsif generated_component_packs_loading_strategy == :async && !PackerUtils.supports_async_loading?
185184
raise ReactOnRails::Error, "**ERROR** #{msg}"
186185
end
187186

spec/dummy/app/views/layouts/application.html.erb

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,10 @@
99

1010
<%= yield :head %>
1111

12-
<%# Conditionally use defer: true for pages with Redux shared stores (inline registration).
13-
Modern apps should use async: true for optimal performance. See docs for details:
14-
docs/building-features/streaming-server-rendering.md %>
15-
<% if uses_redux_shared_store? %>
16-
<%# defer: true required for Redux shared stores with inline component registration %>
17-
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', defer: true) %>
18-
<% else %>
19-
<%# async: true is the recommended approach for modern apps (Shakapacker >= 8.2.0) %>
20-
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', async: true) %>
21-
<% end %>
12+
<%# Use defer: true to ensure proper script execution order.
13+
When using generated component packs (auto_load_bundle), defer ensures
14+
component registrations complete before React hydration begins. %>
15+
<%= javascript_pack_tag('client-bundle', 'data-turbo-track': 'reload', defer: true) %>
2216

2317
<%= csrf_meta_tags %>
2418
</head>

spec/dummy/config/initializers/react_on_rails.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ def self.adjust_props_for_client_side_hydration(component_name, props)
4242
config.components_subdirectory = "startup"
4343
config.auto_load_bundle = true
4444
config.immediate_hydration = false
45-
config.generated_component_packs_loading_strategy = :defer
45+
# Don't explicitly set generated_component_packs_loading_strategy - let it default to :defer
46+
# which ensures generated component packs load and register components before main bundle executes
4647
end

0 commit comments

Comments
 (0)