Module: ReactOnRailsProHelper

Includes:
ScoutApm::Tracer
Defined in:
app/helpers/react_on_rails_pro_helper.rb

Overview

rubocop:disable Metrics/ModuleLength

Instance Method Summary collapse

Instance Method Details

#async_react_component(component_name, options = {}) ⇒ ReactOnRailsPro::AsyncValue

Renders a React component asynchronously, returning an AsyncValue immediately. Multiple async_react_component calls will execute their HTTP rendering requests concurrently instead of sequentially.

Requires the controller to include ReactOnRailsPro::AsyncRendering and call enable_async_react_rendering.

Examples:

<% header = async_react_component("Header", props: @header_props) %>
<% sidebar = async_react_component("Sidebar", props: @sidebar_props) %>
<%= header.value %>
<%= sidebar.value %>

Parameters:

  • component_name (String)

    Name of your registered component

  • options (Hash) (defaults to: {})

    Same options as react_component

Returns:



237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'app/helpers/react_on_rails_pro_helper.rb', line 237

def async_react_component(component_name, options = {})
  unless defined?(@react_on_rails_async_barrier) && @react_on_rails_async_barrier
    raise ReactOnRailsPro::Error,
          "async_react_component requires AsyncRendering concern. " \
          "Include ReactOnRailsPro::AsyncRendering in your controller and call enable_async_react_rendering."
  end

  task = @react_on_rails_async_barrier.async do
    react_component(component_name, options)
  end

  ReactOnRailsPro::AsyncValue.new(task: task)
end

#cached_async_react_component(component_name, raw_options = {}) { ... } ⇒ ReactOnRailsPro::AsyncValue, ReactOnRailsPro::ImmediateAsyncValue

Renders a React component asynchronously with caching support. Cache lookup is synchronous - cache hits return immediately without async. Cache misses trigger async render and cache the result on completion.

All the same options as cached_react_component apply:

  1. You must pass the props as a block (evaluated only on cache miss)

  2. Provide the cache_key option

  3. Optionally provide :cache_options for Rails.cache (expires_in, etc.)

  4. Provide :if or :unless for conditional caching

Examples:

<% card = cached_async_react_component("ProductCard", cache_key: @product) { @product.to_props } %>
<%= card.value %>

Parameters:

  • component_name (String)

    Name of your registered component

  • options (Hash)

    Options including cache_key and cache_options

Yields:

  • Block that returns props (evaluated only on cache miss)

Returns:



270
271
272
273
274
275
# File 'app/helpers/react_on_rails_pro_helper.rb', line 270

def cached_async_react_component(component_name, raw_options = {}, &block)
  ReactOnRailsPro::Utils.with_trace(component_name) do
    check_caching_options!(raw_options, block)
    fetch_async_react_component(component_name, raw_options, &block)
  end
end

#cached_react_component(component_name, raw_options = {}, &block) ⇒ Object

Provide caching support for react_component in a manner akin to Rails fragment caching. All the same options as react_component apply with the following difference:

  1. You must pass the props as a block. This is so that the evaluation of the props is not done if the cache can be used.

  2. Provide the cache_key option cache_key: String or Array (or Proc returning a String or Array) containing your cache keys. If prerender is set to true, the server bundle digest will be included in the cache key. The cache_key value is the same as used for conventional Rails fragment caching.

  3. Optionally provide the ‘:cache_options` key with a value of a hash including as :compress, :expires_in, :race_condition_ttl as documented in the Rails Guides

  4. Provide boolean values for ‘:if` or `:unless` to conditionally use caching.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'app/helpers/react_on_rails_pro_helper.rb', line 51

def cached_react_component(component_name, raw_options = {}, &block)
  ReactOnRailsPro::Utils.with_trace(component_name) do
    check_caching_options!(raw_options, block)

    fetch_react_component(component_name, raw_options) do
      sanitized_options = raw_options
      sanitized_options[:props] = yield
      sanitized_options[:skip_prerender_cache] = true
      sanitized_options[:auto_load_bundle] =
        ReactOnRails.configuration.auto_load_bundle || raw_options[:auto_load_bundle]
      react_component(component_name, sanitized_options)
    end
  end
end

#cached_react_component_hash(component_name, raw_options = {}, &block) ⇒ Object

Provide caching support for react_component_hash in a manner akin to Rails fragment caching. All the same options as react_component_hash apply with the following difference:

  1. You must pass the props as a block. This is so that the evaluation of the props is not done if the cache can be used.

  2. Provide the cache_key option cache_key: String or Array (or Proc returning a String or Array) containing your cache keys. Since prerender is automatically set to true, the server bundle digest will be included in the cache key. The cache_key value is the same as used for conventional Rails fragment caching.

  3. Optionally provide the ‘:cache_options` key with a value of a hash including as :compress, :expires_in, :race_condition_ttl as documented in the Rails Guides

  4. Provide boolean values for ‘:if` or `:unless` to conditionally use caching.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'app/helpers/react_on_rails_pro_helper.rb', line 78

def cached_react_component_hash(component_name, raw_options = {}, &block)
  raw_options[:prerender] = true

  ReactOnRailsPro::Utils.with_trace(component_name) do
    check_caching_options!(raw_options, block)

    fetch_react_component(component_name, raw_options) do
      sanitized_options = raw_options
      sanitized_options[:props] = yield
      sanitized_options[:skip_prerender_cache] = true
      sanitized_options[:auto_load_bundle] =
        ReactOnRails.configuration.auto_load_bundle || raw_options[:auto_load_bundle]
      react_component_hash(component_name, sanitized_options)
    end
  end
end

#cached_stream_react_component(component_name, raw_options = {}, &block) ⇒ Object

Provide caching support for stream_react_component in a manner akin to Rails fragment caching. All the same options as stream_react_component apply with the following differences:

  1. You must pass the props as a block. This is so that the evaluation of the props is not done if the cache can be used.

  2. Provide the cache_key option cache_key: String or Array (or Proc returning a String or Array) containing your cache keys. Since prerender is automatically set to true, the server bundle digest will be included in the cache key. The cache_key value is the same as used for conventional Rails fragment caching.

  3. Optionally provide the ‘:cache_options` key with a value of a hash including as :compress, :expires_in, :race_condition_ttl as documented in the Rails Guides

  4. Provide boolean values for ‘:if` or `:unless` to conditionally use caching.



213
214
215
216
217
218
# File 'app/helpers/react_on_rails_pro_helper.rb', line 213

def cached_stream_react_component(component_name, raw_options = {}, &block)
  ReactOnRailsPro::Utils.with_trace(component_name) do
    check_caching_options!(raw_options, block)
    fetch_stream_react_component(component_name, raw_options, &block)
  end
end

#fetch_react_component(component_name, options) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'app/helpers/react_on_rails_pro_helper.rb', line 11

def fetch_react_component(component_name, options)
  if ReactOnRailsPro::Cache.use_cache?(options)
    cache_key = ReactOnRailsPro::Cache.react_component_cache_key(component_name, options)
    Rails.logger.debug { "React on Rails Pro cache_key is #{cache_key.inspect}" }
    cache_options = options[:cache_options]
    cache_hit = true
    result = Rails.cache.fetch(cache_key, cache_options) do
      cache_hit = false
      yield
    end
    if cache_hit
      render_options = ReactOnRails::ReactComponent::RenderOptions.new(
        react_component_name: component_name,
        options: options
      )
      load_pack_for_generated_component(component_name, render_options)
    end
    # Pass back the cache key in the results only if the result is a Hash
    if result.is_a?(Hash)
      result[:RORP_CACHE_KEY] = cache_key
      result[:RORP_CACHE_HIT] = cache_hit
    end
    result
  else
    yield
  end
end

#rsc_payload_react_component(component_name, options = {}) ⇒ String

Note:

This helper requires React Server Components support to be enabled in your configuration: ReactOnRailsPro.configure do |config|

config.enable_rsc_support = true

end

Note:

You don’t have to deal directly with this helper function - it’s used internally by the

Renders the React Server Component (RSC) payload for a given component. This helper generates a special format designed by React for serializing server components and transmitting them to the client.

Example NDJSON stream:

{"html":"<RSC Payload>","consoleReplayScript":"","hasErrors":false,"isShellReady":true}
{"html":"<RSC Payload>","consoleReplayScript":"console.log('Loading...')","hasErrors":false,"isShellReady":true}

The RSC payload within the html field contains:

  • The component’s rendered output from the server

  • References to client components that need hydration

  • Data props passed to client components

‘rsc_payload_route` helper function. The returned data from this function is used internally by components registered using the `registerServerComponent` function. Don’t use it unless you need more control over the RSC payload generation. To know more about RSC payload, see the following link:

Examples:

Basic usage with a server component

<%= rsc_payload_react_component("ReactServerComponentPage") %>

With props and tracing enabled

<%= rsc_payload_react_component("RSCPostsPage",
      props: { artificialDelay: 1000 },
      trace: true) %>

Parameters:

  • component_name (String)

    The name of the React component to render. This component should be a server component or a mixed component tree containing both server and client components.

  • options (Hash) (defaults to: {})

    Options for rendering the component

Options Hash (options):

  • :props (Hash)

    Props to pass to the component (default: {})

  • :trace (Boolean)

    Enable tracing for debugging (default: false)

  • :id (String)

    Custom DOM ID for the component container (optional)

Returns:

  • (String)

    Returns a Newline Delimited JSON (NDJSON) stream where each line contains a JSON object with:

    • html: The RSC payload containing the rendered server components and client component references

    • consoleReplayScript: JavaScript to replay server-side console logs in the client

    • hasErrors: Boolean indicating if any errors occurred during rendering

    • isShellReady: Boolean indicating if the initial shell is ready for hydration

Raises:

See Also:



188
189
190
191
192
193
194
195
196
197
198
199
# File 'app/helpers/react_on_rails_pro_helper.rb', line 188

def rsc_payload_react_component(component_name, options = {})
  # rsc_payload_react_component doesn't have the prerender option
  # Because setting prerender to false will not do anything
  options[:prerender] = true

  # Extract streaming-specific callback
  on_complete = options.delete(:on_complete)

  consumer_stream_async(on_complete: on_complete) do
    internal_rsc_payload_react_component(component_name, options)
  end
end

#stream_react_component(component_name, options = {}) ⇒ Object

Streams a server-side rendered React component using React’s ‘renderToPipeableStream`. Supports React 18 features like Suspense, concurrent rendering, and selective hydration. Enables progressive rendering and improved performance for large components.

Note: This function can only be used with React on Rails Pro. The view that uses this function must be rendered using the ‘stream_view_containing_react_components` method from the React on Rails Pro gem.

Example of an async React component that can benefit from streaming:

const AsyncComponent = async () =>

const data = await fetchData();
return <div>{data</div>;

};

function App() {

return (
  <Suspense fallback={<div>Loading...</div>}>
    <AsyncComponent />
  </Suspense>
);

}

Any other options are passed to the content tag, including the id.

Parameters:

  • component_name (String)

    Name of your registered component

  • options (Hash) (defaults to: {})

    Options for rendering

Options Hash (options):

  • :props (Hash)

    Props to pass to the react component

  • :dom_id (String)

    DOM ID of the component container

  • :html_options (Hash)

    Options passed to content_tag

  • :trace (Boolean)

    Set to true to add extra debugging information to the HTML

  • :raise_on_prerender_error (Boolean)

    Set to true to raise exceptions during server-side rendering



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/helpers/react_on_rails_pro_helper.rb', line 126

def stream_react_component(component_name, options = {})
  # stream_react_component doesn't have the prerender option
  # Because setting prerender to false is equivalent to calling react_component with prerender: false
  options[:prerender] = true
  options = options.merge(immediate_hydration: true) unless options.key?(:immediate_hydration)

  # Extract streaming-specific callback
  on_complete = options.delete(:on_complete)

  consumer_stream_async(on_complete: on_complete) do
    internal_stream_react_component(component_name, options)
  end
end