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