Skip to content

Commit b654935

Browse files
justin808claude
andcommitted
Rename force_load to immediate_hydration for clarity
BREAKING CHANGE: Rename force_load configuration and API to immediate_hydration - Rename configuration option: force_load → immediate_hydration - Update all method signatures and parameter names - Change data attribute: data-force-load → data-immediate-hydration - Update TypeScript constants and variable names - Revise all documentation and examples - Improve warning messages to focus on license requirement - Default remains false (React on Rails Pro licensed feature) The new name clearly describes the feature's behavior: enabling immediate hydration of React components without waiting for page load, improving time-to-interactive performance. Migration guide: - config.force_load → config.immediate_hydration - react_component(force_load: true) → react_component(immediate_hydration: true) - redux_store(force_load: true) → redux_store(immediate_hydration: true) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 1724436 commit b654935

File tree

9 files changed

+70
-58
lines changed

9 files changed

+70
-58
lines changed

docs/guides/configuration.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,11 @@ ReactOnRails.configure do |config|
226226
# See [15.0.0 Release Notes](docs/release-notes/15.0.0.md) for more details.
227227
# config.defer_generated_component_packs = false
228228

229-
# Default is true
230-
# When true, components hydrate immediately as soon as their server-rendered HTML reaches the client,
231-
# without waiting for the full page load. This improves time-to-interactive performance.
232-
config.force_load = true
229+
# Default is false
230+
# React on Rails Pro (licensed) feature: When true, components hydrate immediately as soon as
231+
# their server-rendered HTML reaches the client, without waiting for the full page load.
232+
# This improves time-to-interactive performance.
233+
config.immediate_hydration = false
233234

234235
################################################################################
235236
# I18N OPTIONS

docs/rails/turbolinks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ document.addEventListener('turbolinks:load', function () {
103103
React on Rails 15 fixes both issues, so if you still have the listener it can be removed (and should be as `reactOnRailsPageLoaded()` is now async).
104104

105105
> [!WARNING]
106-
> Do not use `force_load: false` with Turbolinks if you have async scripts.
106+
> Do not use `immediate_hydration: false` (React on Rails Pro licensed feature) with Turbolinks if you have async scripts.
107107
108108
## Troubleshooting
109109

docs/release-notes/15.0.0.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,15 @@ _The image above demonstrates the dramatic performance improvement:_
5555

5656
- The `defer_generated_component_packs` configuration has been deprecated. Use `generated_component_packs_loading_strategy` instead.
5757
- The `generated_component_packs_loading_strategy` defaults to `:async` for Shakapacker ≥ 8.2.0 and `:sync` for Shakapacker < 8.2.0.
58-
- The `force_load` configuration now defaults to `true`.
59-
- The new default values of `generated_component_packs_loading_strategy: :async` and `force_load: true` work together to optimize component hydration. Components now hydrate as soon as their code and server-rendered HTML are available, without waiting for the full page to load. This parallel processing significantly improves time-to-interactive by eliminating the traditional waterfall of waiting for page load before beginning hydration (It's critical for streamed HTML).
58+
- The `immediate_hydration` configuration now defaults to `false`. **Note: `immediate_hydration` is a React on Rails Pro (licensed) feature.**
59+
- When `generated_component_packs_loading_strategy: :async` and `immediate_hydration: true` are configured together, they optimize component hydration. Components hydrate as soon as their code and server-rendered HTML are available, without waiting for the full page to load. This parallel processing significantly improves time-to-interactive by eliminating the traditional waterfall of waiting for page load before beginning hydration (It's critical for streamed HTML).
6060

6161
- The previous need for deferring scripts to prevent race conditions has been eliminated due to improved hydration handling. Making scripts not defer is critical to execute the hydration scripts early before the page is fully loaded.
62-
- The `force_load` configuration makes `react-on-rails` hydrate components immediately as soon as their server-rendered HTML reaches the client, without waiting for the full page load.
63-
- If you want to keep the previous behavior, you can set `generated_component_packs_loading_strategy: :defer` or `force_load: false` in your `config/initializers/react_on_rails.rb` file.
64-
- You can also keep it for individual components by passing `force_load: false` to `react_component` or `stream_react_component`.
65-
- Redux store now supports `force_load` option, which defaults to `config.force_load` (and so to `true` if that isn't set). If `true`, the Redux store will hydrate immediately as soon as its server-side data reaches the client.
66-
- You can override this behavior for individual Redux stores by calling the `redux_store` helper with `force_load: false`, same as `react_component`.
62+
- The `immediate_hydration` configuration (React on Rails Pro licensed feature) makes `react-on-rails` hydrate components immediately as soon as their server-rendered HTML reaches the client, without waiting for the full page load.
63+
- To enable optimized hydration, you can set `immediate_hydration: true` in your `config/initializers/react_on_rails.rb` file (requires React on Rails Pro license).
64+
- You can also enable it for individual components by passing `immediate_hydration: true` to `react_component` or `stream_react_component`.
65+
- Redux store now supports the `immediate_hydration` option (React on Rails Pro licensed feature), which defaults to `config.immediate_hydration` (and so to `false` if that isn't set). If `true`, the Redux store will hydrate immediately as soon as its server-side data reaches the client.
66+
- You can override this behavior for individual Redux stores by calling the `redux_store` helper with `immediate_hydration: true` or `immediate_hydration: false`, same as `react_component`.
6767

6868
- `ReactOnRails.reactOnRailsPageLoaded()` is now an async function:
6969

lib/react_on_rails/configuration.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ def self.configuration
4646
components_subdirectory: nil,
4747
make_generated_server_bundle_the_entrypoint: false,
4848
defer_generated_component_packs: false,
49-
# forces the loading of React components
50-
force_load: true,
49+
# React on Rails Pro (licensed) feature - enables immediate hydration of React components
50+
immediate_hydration: false,
5151
# Maximum time in milliseconds to wait for client-side component registration after page load.
5252
# If exceeded, an error will be thrown for server-side rendered components not registered on the client.
5353
# Set to 0 to disable the timeout and wait indefinitely for component registration.
@@ -67,7 +67,7 @@ class Configuration
6767
:server_render_method, :random_dom_id, :auto_load_bundle,
6868
:same_bundle_for_client_and_server, :rendering_props_extension,
6969
:make_generated_server_bundle_the_entrypoint,
70-
:generated_component_packs_loading_strategy, :force_load, :rsc_bundle_js_file,
70+
:generated_component_packs_loading_strategy, :immediate_hydration, :rsc_bundle_js_file,
7171
:react_client_manifest_file, :react_server_client_manifest_file, :component_registry_timeout
7272

7373
# rubocop:disable Metrics/AbcSize
@@ -83,7 +83,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
8383
same_bundle_for_client_and_server: nil,
8484
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil,
8585
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
86-
components_subdirectory: nil, auto_load_bundle: nil, force_load: nil,
86+
components_subdirectory: nil, auto_load_bundle: nil, immediate_hydration: nil,
8787
rsc_bundle_js_file: nil, react_client_manifest_file: nil, react_server_client_manifest_file: nil,
8888
component_registry_timeout: nil)
8989
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
@@ -128,7 +128,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
128128
self.auto_load_bundle = auto_load_bundle
129129
self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint
130130
self.defer_generated_component_packs = defer_generated_component_packs
131-
self.force_load = force_load
131+
self.immediate_hydration = immediate_hydration
132132
self.generated_component_packs_loading_strategy = generated_component_packs_loading_strategy
133133
end
134134
# rubocop:enable Metrics/AbcSize

lib/react_on_rails/controller.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@ module Controller
99
# JavaScript code.
1010
# props: Named parameter props which is a Ruby Hash or JSON string which contains the properties
1111
# to pass to the redux store.
12+
# immediate_hydration: React on Rails Pro (licensed) feature. Pass as true if you wish to hydrate this
13+
# store immediately instead of waiting for the page to load.
1214
#
1315
# Be sure to include view helper `redux_store_hydration_data` at the end of your layout or view
1416
# or else there will be no client side hydration of your stores.
15-
def redux_store(store_name, props: {}, force_load: nil)
16-
force_load = ReactOnRails.configuration.force_load if force_load.nil?
17+
def redux_store(store_name, props: {}, immediate_hydration: nil)
18+
immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil?
1719
redux_store_data = { store_name: store_name,
1820
props: props,
19-
force_load: force_load }
21+
immediate_hydration: immediate_hydration }
2022
@registered_stores_defer_render ||= []
2123
@registered_stores_defer_render << redux_store_data
2224
end

lib/react_on_rails/helper.rb

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

1919
COMPONENT_HTML_KEY = "componentHtml"
20+
IMMEDIATE_HYDRATION_PRO_WARNING = "[REACT ON RAILS] The 'immediate_hydration' feature requires a " \
21+
"React on Rails Pro license. " \
22+
"Please visit https://shakacode.com/react-on-rails-pro to learn more."
2023

2124
# react_component_name: can be a React function or class component or a "Render-Function".
2225
# "Render-Functions" differ from a React function in that they take two parameters, the
@@ -58,7 +61,7 @@ def react_component(component_name, options = {})
5861
server_rendered_html = internal_result[:result]["html"]
5962
console_script = internal_result[:result]["consoleReplayScript"]
6063
render_options = internal_result[:render_options]
61-
badge = pro_warning_badge_if_needed(internal_result[:force_load_requested])
64+
badge = pro_warning_badge_if_needed(internal_result[:immediate_hydration_requested])
6265

6366
case server_rendered_html
6467
when String
@@ -128,7 +131,7 @@ def stream_react_component(component_name, options = {})
128131
# stream_react_component doesn't have the prerender option
129132
# Because setting prerender to false is equivalent to calling react_component with prerender: false
130133
options[:prerender] = true
131-
options = options.merge(force_load: true) unless options.key?(:force_load)
134+
options = options.merge(immediate_hydration: true) unless options.key?(:immediate_hydration)
132135
run_stream_inside_fiber do
133136
internal_stream_react_component(component_name, options)
134137
end
@@ -215,7 +218,7 @@ def react_component_hash(component_name, options = {})
215218
server_rendered_html = internal_result[:result]["html"]
216219
console_script = internal_result[:result]["consoleReplayScript"]
217220
render_options = internal_result[:render_options]
218-
badge = pro_warning_badge_if_needed(internal_result[:force_load_requested])
221+
badge = pro_warning_badge_if_needed(internal_result[:immediate_hydration_requested])
219222

220223
if server_rendered_html.is_a?(String) && internal_result[:result]["hasErrors"]
221224
server_rendered_html = { COMPONENT_HTML_KEY => internal_result[:result]["html"] }
@@ -253,16 +256,16 @@ def react_component_hash(component_name, options = {})
253256
# props: Ruby Hash or JSON string which contains the properties to pass to the redux store.
254257
# Options
255258
# defer: false -- pass as true if you wish to render this below your component.
256-
# force_load: false -- pass as true if you wish to hydrate this store immediately instead of
257-
# waiting for the page to load.
258-
def redux_store(store_name, props: {}, defer: false, force_load: nil)
259-
force_load = ReactOnRails.configuration.force_load if force_load.nil?
260-
badge = pro_warning_badge_if_needed(force_load)
261-
force_load = false unless support_pro_features?
259+
# immediate_hydration: false -- React on Rails Pro (licensed) feature. Pass as true if you wish to
260+
# hydrate this store immediately instead of waiting for the page to load.
261+
def redux_store(store_name, props: {}, defer: false, immediate_hydration: nil)
262+
immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil?
263+
badge = pro_warning_badge_if_needed(immediate_hydration)
264+
immediate_hydration = false unless support_pro_features?
262265

263266
redux_store_data = { store_name: store_name,
264267
props: props,
265-
force_load: force_load }
268+
immediate_hydration: immediate_hydration }
266269
if defer
267270
registered_stores_defer_render << redux_store_data
268271
"YOU SHOULD NOT SEE THIS ON YOUR VIEW -- Uses as a code block, like <% redux_store %> " \
@@ -449,20 +452,20 @@ def load_pack_for_generated_component(react_component_name, render_options)
449452

450453
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
451454

455+
# Checks if React on Rails Pro features are available
456+
# @return [Boolean] true if Pro license is valid, false otherwise
452457
def support_pro_features?
453458
ReactOnRails::Utils.react_on_rails_pro_licence_valid?
454459
end
455460

456-
def pro_warning_badge_if_needed(force_load)
457-
return "".html_safe unless force_load
461+
def pro_warning_badge_if_needed(immediate_hydration)
462+
return "".html_safe unless immediate_hydration
458463
return "".html_safe if support_pro_features?
459464

460-
warning_message = "[REACT ON RAILS] The 'force_load' feature requires a React on Rails Pro license. " \
461-
"Please visit https://shakacode.com/react-on-rails-pro to learn more."
462-
puts warning_message
463-
Rails.logger.warn warning_message
465+
puts IMMEDIATE_HYDRATION_PRO_WARNING
466+
Rails.logger.warn IMMEDIATE_HYDRATION_PRO_WARNING
464467

465-
tooltip_text = "The 'force_load' feature requires a React on Rails Pro license. Click to learn more."
468+
tooltip_text = "The 'immediate_hydration' feature requires a React on Rails Pro license. Click to learn more."
466469

467470
badge_html = <<~HTML
468471
<a href="https://shakacode.com/react-on-rails-pro" target="_blank" rel="noopener noreferrer" title="#{tooltip_text}">
@@ -673,8 +676,8 @@ def internal_react_component(react_component_name, options = {})
673676

674677
render_options = create_render_options(react_component_name, options)
675678
# Capture the originally requested value so we can show a badge while still disabling the feature.
676-
force_load_requested = render_options.force_load
677-
render_options.set_option(:force_load, false) unless support_pro_features?
679+
immediate_hydration_requested = render_options.immediate_hydration
680+
render_options.set_option(:immediate_hydration, false) unless support_pro_features?
678681

679682
# Setup the page_loaded_js, which is the same regardless of prerendering or not!
680683
# The reason is that React is smart about not doing extra work if the server rendering did its job.
@@ -687,9 +690,10 @@ def internal_react_component(react_component_name, options = {})
687690
"data-trace" => (render_options.trace ? true : nil),
688691
"data-dom-id" => render_options.dom_id,
689692
"data-store-dependencies" => render_options.store_dependencies&.to_json,
690-
"data-force-load" => (render_options.force_load ? true : nil))
693+
"data-immediate-hydration" =>
694+
(render_options.immediate_hydration ? true : nil))
691695

692-
if render_options.force_load
696+
if render_options.immediate_hydration
693697
component_specification_tag.concat(
694698
content_tag(:script, %(
695699
typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsComponentLoaded('#{render_options.dom_id}');
@@ -705,7 +709,7 @@ def internal_react_component(react_component_name, options = {})
705709
render_options: render_options,
706710
tag: component_specification_tag,
707711
result: result,
708-
force_load_requested: force_load_requested
712+
immediate_hydration_requested: immediate_hydration_requested
709713
}
710714
end
711715

@@ -714,9 +718,10 @@ def render_redux_store_data(redux_store_data)
714718
json_safe_and_pretty(redux_store_data[:props]).html_safe,
715719
type: "application/json",
716720
"data-js-react-on-rails-store" => redux_store_data[:store_name].html_safe,
717-
"data-force-load" => (redux_store_data[:force_load] ? true : nil))
721+
"data-immediate-hydration" =>
722+
(redux_store_data[:immediate_hydration] ? true : nil))
718723

719-
if redux_store_data[:force_load]
724+
if redux_store_data[:immediate_hydration]
720725
store_hydration_data.concat(
721726
content_tag(:script, <<~JS.strip_heredoc.html_safe
722727
typeof ReactOnRails === 'object' && ReactOnRails.reactOnRailsStoreLoaded('#{redux_store_data[:store_name]}');

lib/react_on_rails/react_component/render_options.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ def logging_on_server
9595
retrieve_configuration_value_for(:logging_on_server)
9696
end
9797

98-
def force_load
99-
retrieve_configuration_value_for(:force_load)
98+
def immediate_hydration
99+
retrieve_configuration_value_for(:immediate_hydration)
100100
end
101101

102102
def to_s

node_package/src/ClientSideRenderer.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import * as ComponentRegistry from './ComponentRegistry.ts';
1414
import { onPageLoaded } from './pageLifecycle.ts';
1515

1616
const REACT_ON_RAILS_STORE_ATTRIBUTE = 'data-js-react-on-rails-store';
17+
const IMMEDIATE_HYDRATION_PRO_WARNING =
18+
"[REACT ON RAILS] The 'immediate_hydration' feature requires a React on Rails Pro license. " +
19+
'Please visit https://shakacode.com/react-on-rails-pro to get a license.';
1720

1821
async function delegateToRenderer(
1922
componentObj: RegisteredComponent,
@@ -79,18 +82,19 @@ class ComponentRenderer {
7982
* delegates to a renderer registered by the user.
8083
*/
8184
private async render(el: Element, railsContext: RailsContext): Promise<void> {
82-
const isComponentForceLoaded = el.getAttribute('data-force-load') === 'true';
83-
if (!railsContext.rorPro && (isComponentForceLoaded || document.readyState === 'loading')) {
84-
console.warn(
85-
"[REACT ON RAILS] The 'force_load' feature is being used without a React on Rails Pro license. " +
86-
"That's not allowed. " +
87-
'Please visit https://shakacode.com/react-on-rails-pro to get a license.',
88-
);
85+
const isImmediateHydrationRequested = el.getAttribute('data-immediate-hydration') === 'true';
86+
const hasProLicense = railsContext.rorPro;
87+
88+
// Handle immediate_hydration feature usage without Pro license
89+
if (isImmediateHydrationRequested && !hasProLicense) {
90+
console.warn(IMMEDIATE_HYDRATION_PRO_WARNING);
8991

90-
// Wait for the page to be loaded before continuing
91-
await new Promise<void>((resolve) => {
92-
onPageLoaded(resolve);
93-
});
92+
// Fallback to standard behavior: wait for page load before hydrating
93+
if (document.readyState === 'loading') {
94+
await new Promise<void>((resolve) => {
95+
onPageLoaded(resolve);
96+
});
97+
}
9498
}
9599

96100
// This must match lib/react_on_rails/helper.rb

spec/dummy/spec/support/selenium_logger.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
err_msg.include?("SharedArrayBuffer will require cross-origin isolation") ||
2828
err_msg.include?("You are currently using minified code outside of NODE_ENV === \\\"production\\\"") ||
2929
err_msg.include?("This version of ChromeDriver has not been tested with Chrome version") ||
30-
err_msg.include?("The 'force_load' feature is being used without a React on Rails Pro license")
30+
err_msg.include?("The 'immediate_hydration' feature requires a React on Rails Pro license")
3131
end
3232

3333
raise("Java Script Error(s) on the page:\n\n#{clean_errors.join("\n")}") if clean_errors.present?

0 commit comments

Comments
 (0)