Skip to content

Commit 8a91778

Browse files
immediate hydration, generated_component_packs_loading_strategy (#1993)
* deprecate immediate_hydration config (#1993) * immediate_hydration only at component level and defaults to true for pro * async not used for non-pro Why Visual warning badges disrupted user experience when Pro features unavailable. Summary Removed intrusive Pro warning badge. Deprecated config.immediate_hydration in favor of automatic Pro license detection. Added thread-safe deprecation warnings with comprehensive test coverage. Impact - Existing: Pro users get immediate hydration automatically - New: Non-Pro users no longer see warning badges, silent fallback instead Risks Breaking: config.immediate_hydration removed (deprecation warnings guide migration). Component-level overrides still supported.
1 parent fffa737 commit 8a91778

File tree

15 files changed

+470
-256
lines changed

15 files changed

+470
-256
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,30 @@ Changes since the last non-beta release.
4949

5050
- **Shakapacker 9.2.0 Upgrade**: Upgraded Shakapacker from 9.1.0 to 9.2.0. This minor version update adds a new `bin/shakapacker-config` utility for debugging webpack/rspack configurations with doctor mode, save mode, and stdout mode options. Supports YAML, JSON, and Node.js inspect output formats. by [justin808](https://github.com/justin808).
5151

52+
- **Removed Pro Warning Badge**: Removed the visual warning badge that appeared when non-Pro users attempted to enable Pro-only features like `immediate_hydration`. Pro features are now silently disabled when a Pro license is not available, providing a cleaner user experience without intrusive warning banners. [PR #1993](https://github.com/shakacode/react_on_rails/pull/1993) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
53+
54+
- **`immediate_hydration` now automatically enabled for Pro users**: The `config.immediate_hydration` configuration option has been removed. Immediate hydration is now automatically enabled for React on Rails Pro users and disabled for non-Pro users, simplifying configuration while providing optimal performance by default. Component-level overrides are still supported via the `immediate_hydration` parameter on `react_component`, `redux_store`, and `stream_react_component` helpers. [PR 1997](https://github.com/shakacode/react_on_rails/pull/1997) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
55+
56+
- **`generated_component_packs_loading_strategy` now defaults based on Pro license**: When using Shakapacker >= 8.2.0, the default loading strategy is now `:async` for Pro users and `:defer` for non-Pro users. This provides optimal performance for Pro users while maintaining compatibility for non-Pro users. You can still explicitly set the strategy in your configuration. [PR #1993](https://github.com/shakacode/react_on_rails/pull/1993) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
57+
5258
#### Bug Fixes
5359

5460
- **Use as Git dependency**: All packages can now be installed as Git dependencies. This is useful for development and testing purposes. See [CONTRIBUTING.md](./CONTRIBUTING.md#git-dependencies) for documentation. [PR #1873](https://github.com/shakacode/react_on_rails/pull/1873) by [alexeyr-ci2](https://github.com/alexeyr-ci2).
5561

5662
#### Breaking Changes
5763

64+
- **`config.immediate_hydration` configuration removed**: The `config.immediate_hydration` setting in `config/initializers/react_on_rails.rb` has been removed. Immediate hydration is now automatically enabled for React on Rails Pro users and automatically disabled for non-Pro users.
65+
66+
**Migration steps:**
67+
68+
- Remove any `config.immediate_hydration = true` or `config.immediate_hydration = false` lines from your `config/initializers/react_on_rails.rb` file
69+
- Pro users: No action needed - immediate hydration is now enabled automatically for optimal performance
70+
- Non-Pro users: No action needed - standard hydration behavior continues to work as before
71+
- Component-level overrides: You can still override behavior per-component using `react_component("MyComponent", immediate_hydration: false)` or `redux_store("MyStore", immediate_hydration: true)`
72+
- If a non-Pro user explicitly sets `immediate_hydration: true` on a component or store, a warning will be logged and it will be enforced to fall back to standard hydration (the value will be overridden to `false`)
73+
74+
[PR 1997](https://github.com/shakacode/react_on_rails/pull/1997) by [AbanoubGhadban](https://github.com/AbanoubGhadban).
75+
5876
- **React on Rails Core Package**: Several Pro-only methods have been removed from the core package and are now exclusively available in the `react-on-rails-pro` package. If you're using any of the following methods, you'll need to migrate to React on Rails Pro:
5977
- `getOrWaitForComponent()`
6078
- `getOrWaitForStore()`

lib/react_on_rails/configuration.rb

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ def self.configuration
4141
components_subdirectory: nil,
4242
make_generated_server_bundle_the_entrypoint: false,
4343
defer_generated_component_packs: false,
44-
# React on Rails Pro (licensed) feature - enables immediate hydration of React components
45-
immediate_hydration: false,
4644
# Maximum time in milliseconds to wait for client-side component registration after page load.
4745
# If exceeded, an error will be thrown for server-side rendered components not registered on the client.
4846
# Set to 0 to disable the timeout and wait indefinitely for component registration.
@@ -64,10 +62,55 @@ class Configuration
6462
:server_render_method, :random_dom_id, :auto_load_bundle,
6563
:same_bundle_for_client_and_server, :rendering_props_extension,
6664
:make_generated_server_bundle_the_entrypoint,
67-
:generated_component_packs_loading_strategy, :immediate_hydration,
65+
:generated_component_packs_loading_strategy,
6866
:component_registry_timeout,
6967
:server_bundle_output_path, :enforce_private_server_bundles
7068

69+
# Class instance variable and mutex to track if deprecation warning has been shown
70+
# Using mutex to ensure thread-safety in multi-threaded environments
71+
@immediate_hydration_warned = false
72+
@immediate_hydration_mutex = Mutex.new
73+
74+
class << self
75+
attr_accessor :immediate_hydration_warned, :immediate_hydration_mutex
76+
end
77+
78+
# Deprecated: immediate_hydration configuration has been removed
79+
def immediate_hydration=(value)
80+
warned = false
81+
self.class.immediate_hydration_mutex.synchronize do
82+
warned = self.class.immediate_hydration_warned
83+
self.class.immediate_hydration_warned = true unless warned
84+
end
85+
86+
return if warned
87+
88+
Rails.logger.warn <<~WARNING
89+
[REACT ON RAILS] The 'config.immediate_hydration' configuration option is deprecated and no longer used.
90+
Immediate hydration is now automatically enabled for React on Rails Pro users.
91+
Please remove 'config.immediate_hydration = #{value}' from your config/initializers/react_on_rails.rb file.
92+
See CHANGELOG.md for migration instructions.
93+
WARNING
94+
end
95+
96+
def immediate_hydration
97+
warned = false
98+
self.class.immediate_hydration_mutex.synchronize do
99+
warned = self.class.immediate_hydration_warned
100+
self.class.immediate_hydration_warned = true unless warned
101+
end
102+
103+
return nil if warned
104+
105+
Rails.logger.warn <<~WARNING
106+
[REACT ON RAILS] The 'config.immediate_hydration' configuration option is deprecated and no longer used.
107+
Immediate hydration is now automatically enabled for React on Rails Pro users.
108+
Please remove any references to 'config.immediate_hydration' from your config/initializers/react_on_rails.rb file.
109+
See CHANGELOG.md for migration instructions.
110+
WARNING
111+
nil
112+
end
113+
71114
# rubocop:disable Metrics/AbcSize
72115
def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil,
73116
replay_console: nil, make_generated_server_bundle_the_entrypoint: nil,
@@ -81,7 +124,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
81124
same_bundle_for_client_and_server: nil,
82125
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil,
83126
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
84-
components_subdirectory: nil, auto_load_bundle: nil, immediate_hydration: nil,
127+
components_subdirectory: nil, auto_load_bundle: nil,
85128
component_registry_timeout: nil, server_bundle_output_path: nil, enforce_private_server_bundles: nil)
86129
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
87130
self.generated_assets_dirs = generated_assets_dirs
@@ -122,7 +165,6 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
122165
self.auto_load_bundle = auto_load_bundle
123166
self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint
124167
self.defer_generated_component_packs = defer_generated_component_packs
125-
self.immediate_hydration = immediate_hydration
126168
self.generated_component_packs_loading_strategy = generated_component_packs_loading_strategy
127169
self.server_bundle_output_path = server_bundle_output_path
128170
self.enforce_private_server_bundles = enforce_private_server_bundles
@@ -177,7 +219,8 @@ def validate_generated_component_packs_loading_strategy
177219
2. Upgrade to Shakapacker v8.2.0 or above to enable async script loading
178220
MSG
179221
if PackerUtils.supports_async_loading?
180-
self.generated_component_packs_loading_strategy ||= :async
222+
# Default based on Pro license: Pro users get :async, non-Pro users get :defer
223+
self.generated_component_packs_loading_strategy ||= (Utils.react_on_rails_pro? ? :async : :defer)
181224
elsif generated_component_packs_loading_strategy.nil?
182225
Rails.logger.warn("**WARNING** #{msg}")
183226
self.generated_component_packs_loading_strategy = :sync

lib/react_on_rails/controller.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ 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.
12+
# immediate_hydration: React on Rails Pro (licensed) feature. When nil (default), Pro users get
13+
# immediate hydration, non-Pro users don't. Can be explicitly overridden.
1414
#
1515
# Be sure to include view helper `redux_store_hydration_data` at the end of your layout or view
1616
# or else there will be no client side hydration of your stores.
1717
def redux_store(store_name, props: {}, immediate_hydration: nil)
18-
immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil?
18+
immediate_hydration = ReactOnRails::Utils.normalize_immediate_hydration(immediate_hydration, store_name, "Store")
1919
redux_store_data = { store_name: store_name,
2020
props: props,
2121
immediate_hydration: immediate_hydration }

lib/react_on_rails/doctor.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -732,10 +732,12 @@ def analyze_performance_config(content)
732732
auto_load_match = content.match(/config\.auto_load_bundle\s*=\s*([^\s\n,]+)/)
733733
checker.add_info(" auto_load_bundle: #{auto_load_match[1]}") if auto_load_match
734734

735-
# Immediate hydration (Pro feature)
735+
# Deprecated immediate_hydration setting
736736
immediate_hydration_match = content.match(/config\.immediate_hydration\s*=\s*([^\s\n,]+)/)
737737
if immediate_hydration_match
738-
checker.add_info(" immediate_hydration: #{immediate_hydration_match[1]} (React on Rails Pro)")
738+
checker.add_warning(" ⚠️ immediate_hydration: #{immediate_hydration_match[1]} (DEPRECATED)")
739+
checker.add_info(" 💡 This setting is no longer used. Immediate hydration is now automatic for Pro users.")
740+
checker.add_info(" 💡 Remove this line from your config/initializers/react_on_rails.rb file.")
739741
end
740742

741743
# Component registry timeout

lib/react_on_rails/helper.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,10 @@ def react_component_hash(component_name, options = {})
155155
# props: Ruby Hash or JSON string which contains the properties to pass to the redux store.
156156
# Options
157157
# defer: false -- pass as true if you wish to render this below your component.
158-
# immediate_hydration: false -- React on Rails Pro (licensed) feature. Pass as true if you wish to
159-
# hydrate this store immediately instead of waiting for the page to load.
158+
# immediate_hydration: nil -- React on Rails Pro (licensed) feature. When nil (default), Pro users
159+
# get immediate hydration, non-Pro users don't. Can be explicitly overridden.
160160
def redux_store(store_name, props: {}, defer: false, immediate_hydration: nil)
161-
immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil?
161+
immediate_hydration = ReactOnRails::Utils.normalize_immediate_hydration(immediate_hydration, store_name, "Store")
162162

163163
redux_store_data = { store_name: store_name,
164164
props: props,

lib/react_on_rails/pro_helper.rb

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
module ReactOnRails
44
module ProHelper
5-
IMMEDIATE_HYDRATION_PRO_WARNING = "[REACT ON RAILS] The 'immediate_hydration' feature requires a " \
6-
"React on Rails Pro license. " \
7-
"Please visit https://shakacode.com/react-on-rails-pro to learn more."
8-
95
# Generates the complete component specification script tag.
106
# Handles both immediate hydration (Pro feature) and standard cases.
117
def generate_component_script(render_options)
@@ -36,17 +32,12 @@ def generate_component_script(render_options)
3632
component_specification_tag
3733
end
3834

39-
pro_warning_badge = pro_warning_badge_if_needed(render_options.explicitly_disabled_pro_options)
40-
"#{pro_warning_badge}\n#{spec_tag}".html_safe
35+
spec_tag.html_safe
4136
end
4237

4338
# Generates the complete store hydration script tag.
4439
# Handles both immediate hydration (Pro feature) and standard cases.
4540
def generate_store_script(redux_store_data)
46-
pro_options_check_result = ReactOnRails::ProUtils.disable_pro_render_options_if_not_licensed(redux_store_data)
47-
redux_store_data = pro_options_check_result[:raw_options]
48-
explicitly_disabled_pro_options = pro_options_check_result[:explicitly_disabled_pro_options]
49-
5041
store_hydration_data = content_tag(:script,
5142
json_safe_and_pretty(redux_store_data[:props]).html_safe,
5243
type: "application/json",
@@ -67,40 +58,7 @@ def generate_store_script(redux_store_data)
6758
store_hydration_data
6859
end
6960

70-
pro_warning_badge = pro_warning_badge_if_needed(explicitly_disabled_pro_options)
71-
"#{pro_warning_badge}\n#{store_hydration_scripts}".html_safe
72-
end
73-
74-
def pro_warning_badge_if_needed(explicitly_disabled_pro_options)
75-
return "" unless explicitly_disabled_pro_options.any?
76-
77-
disabled_features_message = disabled_pro_features_message(explicitly_disabled_pro_options)
78-
warning_message = "[REACT ON RAILS] #{disabled_features_message}\n" \
79-
"Please visit https://shakacode.com/react-on-rails-pro to learn more."
80-
puts warning_message
81-
Rails.logger.warn warning_message
82-
83-
tooltip_text = "#{disabled_features_message} Click to learn more."
84-
85-
<<~HTML.strip
86-
<a href="https://shakacode.com/react-on-rails-pro" target="_blank" rel="noopener noreferrer" title="#{tooltip_text}">
87-
<div style="position: fixed; top: 0; right: 0; width: 180px; height: 180px; overflow: hidden; z-index: 9999; pointer-events: none;">
88-
<div style="position: absolute; top: 50px; right: -40px; transform: rotate(45deg); background-color: rgba(220, 53, 69, 0.85); color: white; padding: 7px 40px; text-align: center; font-weight: bold; font-family: sans-serif; font-size: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.3); pointer-events: auto;">
89-
React On Rails Pro Required
90-
</div>
91-
</div>
92-
</a>
93-
HTML
94-
end
95-
96-
def disabled_pro_features_message(explicitly_disabled_pro_options)
97-
return "".html_safe unless explicitly_disabled_pro_options.any?
98-
99-
feature_list = explicitly_disabled_pro_options.join(", ")
100-
feature_word = explicitly_disabled_pro_options.size == 1 ? "feature" : "features"
101-
"The '#{feature_list}' #{feature_word} " \
102-
"#{explicitly_disabled_pro_options.size == 1 ? 'requires' : 'require'} a " \
103-
"React on Rails Pro license. "
61+
store_hydration_scripts.html_safe
10462
end
10563
end
10664
end

lib/react_on_rails/pro_utils.rb

Lines changed: 0 additions & 37 deletions
This file was deleted.

lib/react_on_rails/react_component/render_options.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# frozen_string_literal: true
22

33
require "react_on_rails/utils"
4-
require "react_on_rails/pro_utils"
54

65
module ReactOnRails
76
module ReactComponent
@@ -15,13 +14,10 @@ class RenderOptions
1514
# TODO: remove the required for named params
1615
def initialize(react_component_name: required("react_component_name"), options: required("options"))
1716
@react_component_name = react_component_name.camelize
18-
19-
result = ReactOnRails::ProUtils.disable_pro_render_options_if_not_licensed(options)
20-
@options = result[:raw_options]
21-
@explicitly_disabled_pro_options = result[:explicitly_disabled_pro_options]
17+
@options = options
2218
end
2319

24-
attr_reader :react_component_name, :explicitly_disabled_pro_options
20+
attr_reader :react_component_name
2521

2622
def throw_js_errors
2723
options.fetch(:throw_js_errors, false)
@@ -100,7 +96,11 @@ def logging_on_server
10096
end
10197

10298
def immediate_hydration
103-
retrieve_configuration_value_for(:immediate_hydration)
99+
ReactOnRails::Utils.normalize_immediate_hydration(
100+
options[:immediate_hydration],
101+
react_component_name,
102+
"Component"
103+
)
104104
end
105105

106106
def to_s

0 commit comments

Comments
 (0)