Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,30 @@ Changes since the last non-beta release.

- **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).

- **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).

- **`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).

- **`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).

#### Bug Fixes

- **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).

#### Breaking Changes

- **`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.

**Migration steps:**

- Remove any `config.immediate_hydration = true` or `config.immediate_hydration = false` lines from your `config/initializers/react_on_rails.rb` file
- Pro users: No action needed - immediate hydration is now enabled automatically for optimal performance
- Non-Pro users: No action needed - standard hydration behavior continues to work as before
- Component-level overrides: You can still override behavior per-component using `react_component("MyComponent", immediate_hydration: false)` or `redux_store("MyStore", immediate_hydration: true)`
- 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`)

[PR 1997](https://github.com/shakacode/react_on_rails/pull/1997) by [AbanoubGhadban](https://github.com/AbanoubGhadban).

- **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:
- `getOrWaitForComponent()`
- `getOrWaitForStore()`
Expand Down
55 changes: 49 additions & 6 deletions lib/react_on_rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ def self.configuration
components_subdirectory: nil,
make_generated_server_bundle_the_entrypoint: false,
defer_generated_component_packs: false,
# React on Rails Pro (licensed) feature - enables immediate hydration of React components
immediate_hydration: false,
# Maximum time in milliseconds to wait for client-side component registration after page load.
# If exceeded, an error will be thrown for server-side rendered components not registered on the client.
# Set to 0 to disable the timeout and wait indefinitely for component registration.
Expand All @@ -64,10 +62,55 @@ class Configuration
:server_render_method, :random_dom_id, :auto_load_bundle,
:same_bundle_for_client_and_server, :rendering_props_extension,
:make_generated_server_bundle_the_entrypoint,
:generated_component_packs_loading_strategy, :immediate_hydration,
:generated_component_packs_loading_strategy,
:component_registry_timeout,
:server_bundle_output_path, :enforce_private_server_bundles

# Class instance variable and mutex to track if deprecation warning has been shown
# Using mutex to ensure thread-safety in multi-threaded environments
@immediate_hydration_warned = false
@immediate_hydration_mutex = Mutex.new

class << self
attr_accessor :immediate_hydration_warned, :immediate_hydration_mutex
end

# Deprecated: immediate_hydration configuration has been removed
def immediate_hydration=(value)
warned = false
self.class.immediate_hydration_mutex.synchronize do
warned = self.class.immediate_hydration_warned
self.class.immediate_hydration_warned = true unless warned
end

return if warned

Rails.logger.warn <<~WARNING
[REACT ON RAILS] The 'config.immediate_hydration' configuration option is deprecated and no longer used.
Immediate hydration is now automatically enabled for React on Rails Pro users.
Please remove 'config.immediate_hydration = #{value}' from your config/initializers/react_on_rails.rb file.
See CHANGELOG.md for migration instructions.
WARNING
end

def immediate_hydration
warned = false
self.class.immediate_hydration_mutex.synchronize do
warned = self.class.immediate_hydration_warned
self.class.immediate_hydration_warned = true unless warned
end

return nil if warned

Rails.logger.warn <<~WARNING
[REACT ON RAILS] The 'config.immediate_hydration' configuration option is deprecated and no longer used.
Immediate hydration is now automatically enabled for React on Rails Pro users.
Please remove any references to 'config.immediate_hydration' from your config/initializers/react_on_rails.rb file.
See CHANGELOG.md for migration instructions.
WARNING
nil
end

# rubocop:disable Metrics/AbcSize
def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender: nil,
replay_console: nil, make_generated_server_bundle_the_entrypoint: nil,
Expand All @@ -81,7 +124,7 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
same_bundle_for_client_and_server: nil,
i18n_dir: nil, i18n_yml_dir: nil, i18n_output_format: nil, i18n_yml_safe_load_options: nil,
random_dom_id: nil, server_render_method: nil, rendering_props_extension: nil,
components_subdirectory: nil, auto_load_bundle: nil, immediate_hydration: nil,
components_subdirectory: nil, auto_load_bundle: nil,
component_registry_timeout: nil, server_bundle_output_path: nil, enforce_private_server_bundles: nil)
self.node_modules_location = node_modules_location.present? ? node_modules_location : Rails.root
self.generated_assets_dirs = generated_assets_dirs
Expand Down Expand Up @@ -122,7 +165,6 @@ def initialize(node_modules_location: nil, server_bundle_js_file: nil, prerender
self.auto_load_bundle = auto_load_bundle
self.make_generated_server_bundle_the_entrypoint = make_generated_server_bundle_the_entrypoint
self.defer_generated_component_packs = defer_generated_component_packs
self.immediate_hydration = immediate_hydration
self.generated_component_packs_loading_strategy = generated_component_packs_loading_strategy
self.server_bundle_output_path = server_bundle_output_path
self.enforce_private_server_bundles = enforce_private_server_bundles
Expand Down Expand Up @@ -177,7 +219,8 @@ def validate_generated_component_packs_loading_strategy
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
# Default based on Pro license: Pro users get :async, non-Pro users get :defer
self.generated_component_packs_loading_strategy ||= (Utils.react_on_rails_pro? ? :async : :defer)
elsif generated_component_packs_loading_strategy.nil?
Rails.logger.warn("**WARNING** #{msg}")
self.generated_component_packs_loading_strategy = :sync
Expand Down
6 changes: 3 additions & 3 deletions lib/react_on_rails/controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ module Controller
# JavaScript code.
# props: Named parameter props which is a Ruby Hash or JSON string which contains the properties
# to pass to the redux store.
# immediate_hydration: React on Rails Pro (licensed) feature. Pass as true if you wish to hydrate this
# store immediately instead of waiting for the page to load.
# immediate_hydration: React on Rails Pro (licensed) feature. When nil (default), Pro users get
# immediate hydration, non-Pro users don't. Can be explicitly overridden.
#
# Be sure to include view helper `redux_store_hydration_data` at the end of your layout or view
# or else there will be no client side hydration of your stores.
def redux_store(store_name, props: {}, immediate_hydration: nil)
immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil?
immediate_hydration = ReactOnRails::Utils.normalize_immediate_hydration(immediate_hydration, store_name, "Store")
redux_store_data = { store_name: store_name,
props: props,
immediate_hydration: immediate_hydration }
Expand Down
6 changes: 4 additions & 2 deletions lib/react_on_rails/doctor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -732,10 +732,12 @@ def analyze_performance_config(content)
auto_load_match = content.match(/config\.auto_load_bundle\s*=\s*([^\s\n,]+)/)
checker.add_info(" auto_load_bundle: #{auto_load_match[1]}") if auto_load_match

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

# Component registry timeout
Expand Down
6 changes: 3 additions & 3 deletions lib/react_on_rails/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,10 @@ def react_component_hash(component_name, options = {})
# props: Ruby Hash or JSON string which contains the properties to pass to the redux store.
# Options
# defer: false -- pass as true if you wish to render this below your component.
# immediate_hydration: false -- React on Rails Pro (licensed) feature. Pass as true if you wish to
# hydrate this store immediately instead of waiting for the page to load.
# immediate_hydration: nil -- React on Rails Pro (licensed) feature. When nil (default), Pro users
# get immediate hydration, non-Pro users don't. Can be explicitly overridden.
def redux_store(store_name, props: {}, defer: false, immediate_hydration: nil)
immediate_hydration = ReactOnRails.configuration.immediate_hydration if immediate_hydration.nil?
immediate_hydration = ReactOnRails::Utils.normalize_immediate_hydration(immediate_hydration, store_name, "Store")

redux_store_data = { store_name: store_name,
props: props,
Expand Down
46 changes: 2 additions & 44 deletions lib/react_on_rails/pro_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

module ReactOnRails
module ProHelper
IMMEDIATE_HYDRATION_PRO_WARNING = "[REACT ON RAILS] The 'immediate_hydration' feature requires a " \
"React on Rails Pro license. " \
"Please visit https://shakacode.com/react-on-rails-pro to learn more."

# Generates the complete component specification script tag.
# Handles both immediate hydration (Pro feature) and standard cases.
def generate_component_script(render_options)
Expand Down Expand Up @@ -36,17 +32,12 @@ def generate_component_script(render_options)
component_specification_tag
end

pro_warning_badge = pro_warning_badge_if_needed(render_options.explicitly_disabled_pro_options)
"#{pro_warning_badge}\n#{spec_tag}".html_safe
spec_tag.html_safe
end

# Generates the complete store hydration script tag.
# Handles both immediate hydration (Pro feature) and standard cases.
def generate_store_script(redux_store_data)
pro_options_check_result = ReactOnRails::ProUtils.disable_pro_render_options_if_not_licensed(redux_store_data)
redux_store_data = pro_options_check_result[:raw_options]
explicitly_disabled_pro_options = pro_options_check_result[:explicitly_disabled_pro_options]

store_hydration_data = content_tag(:script,
json_safe_and_pretty(redux_store_data[:props]).html_safe,
type: "application/json",
Expand All @@ -67,40 +58,7 @@ def generate_store_script(redux_store_data)
store_hydration_data
end

pro_warning_badge = pro_warning_badge_if_needed(explicitly_disabled_pro_options)
"#{pro_warning_badge}\n#{store_hydration_scripts}".html_safe
end

def pro_warning_badge_if_needed(explicitly_disabled_pro_options)
return "" unless explicitly_disabled_pro_options.any?

disabled_features_message = disabled_pro_features_message(explicitly_disabled_pro_options)
warning_message = "[REACT ON RAILS] #{disabled_features_message}\n" \
"Please visit https://shakacode.com/react-on-rails-pro to learn more."
puts warning_message
Rails.logger.warn warning_message

tooltip_text = "#{disabled_features_message} Click to learn more."

<<~HTML.strip
<a href="https://shakacode.com/react-on-rails-pro" target="_blank" rel="noopener noreferrer" title="#{tooltip_text}">
<div style="position: fixed; top: 0; right: 0; width: 180px; height: 180px; overflow: hidden; z-index: 9999; pointer-events: none;">
<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;">
React On Rails Pro Required
</div>
</div>
</a>
HTML
end

def disabled_pro_features_message(explicitly_disabled_pro_options)
return "".html_safe unless explicitly_disabled_pro_options.any?

feature_list = explicitly_disabled_pro_options.join(", ")
feature_word = explicitly_disabled_pro_options.size == 1 ? "feature" : "features"
"The '#{feature_list}' #{feature_word} " \
"#{explicitly_disabled_pro_options.size == 1 ? 'requires' : 'require'} a " \
"React on Rails Pro license. "
store_hydration_scripts.html_safe
end
end
end
37 changes: 0 additions & 37 deletions lib/react_on_rails/pro_utils.rb

This file was deleted.

14 changes: 7 additions & 7 deletions lib/react_on_rails/react_component/render_options.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true

require "react_on_rails/utils"
require "react_on_rails/pro_utils"

module ReactOnRails
module ReactComponent
Expand All @@ -15,13 +14,10 @@ class RenderOptions
# TODO: remove the required for named params
def initialize(react_component_name: required("react_component_name"), options: required("options"))
@react_component_name = react_component_name.camelize

result = ReactOnRails::ProUtils.disable_pro_render_options_if_not_licensed(options)
@options = result[:raw_options]
@explicitly_disabled_pro_options = result[:explicitly_disabled_pro_options]
@options = options
end

attr_reader :react_component_name, :explicitly_disabled_pro_options
attr_reader :react_component_name

def throw_js_errors
options.fetch(:throw_js_errors, false)
Expand Down Expand Up @@ -100,7 +96,11 @@ def logging_on_server
end

def immediate_hydration
retrieve_configuration_value_for(:immediate_hydration)
ReactOnRails::Utils.normalize_immediate_hydration(
options[:immediate_hydration],
react_component_name,
"Component"
)
end

def to_s
Expand Down
Loading
Loading