Module: ReactOnRailsPro::Stream

Extended by:
ActiveSupport::Concern
Defined in:
lib/react_on_rails_pro/concerns/stream.rb

Instance Method Summary collapse

Instance Method Details

#stream_view_containing_react_components(template:, close_stream_at_end: true, **render_options) ⇒ Object

Note:

The ‘stream_react_component` helper is defined in the react_on_rails gem. For more details, refer to `lib/react_on_rails/helper.rb` in the react_on_rails repository.

Streams React components within a specified template to the client.

components must be added to the view using the ‘stream_react_component` helper.

Examples:

stream_view_containing_react_components(template: 'path/to/your/template')
stream_view_containing_react_components(
  template: 'path/to/your/template',
  close_stream_at_end: false,
  layout: false
)

Parameters:

  • template (String)

    The path to the template file to be streamed.

  • close_stream_at_end (Boolean) (defaults to: true)

    Whether to automatically close the stream after rendering (default: true).

  • render_options (Hash)

    Additional options to pass to ‘render_to_string`.

See Also:

  • ReactOnRails::Helper#stream_react_component


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/react_on_rails_pro/concerns/stream.rb', line 33

def stream_view_containing_react_components(template:, close_stream_at_end: true, **render_options)
  require "async"
  require "async/barrier"
  require "async/limited_queue"

  Sync do |parent_task|
    # Initialize async primitives for concurrent component streaming
    @async_barrier = Async::Barrier.new
    buffer_size = ReactOnRailsPro.configuration.concurrent_component_streaming_buffer_size
    @main_output_queue = Async::LimitedQueue.new(buffer_size)

    # Render template - components will start streaming immediately.
    # If a shell error occurs, consumer_stream_async raises PrerenderError here
    # (BEFORE the response is committed), enabling a proper HTTP redirect.
    template_string = render_to_string(template: template, **render_options)
    # View may contain extra newlines, chunk already contains a newline
    # Having multiple newlines between chunks causes hydration errors
    # So we strip extra newlines from the template string and add a single newline
    response.stream.write(template_string)

    drain_streams_concurrently(parent_task)
    # Do not close the response stream in an ensure block.
    # If an error occurs we may need the stream open to send diagnostic/error details
    # (for example, ApplicationController#rescue_from in the dummy app).
    response.stream.close if close_stream_at_end
  rescue StandardError
    # Stop all streaming tasks to prevent leaked async work.
    # For pre-commit errors (e.g., shell error raised during render_to_string),
    # the barrier may still have pending tasks that must be cancelled.
    # For post-commit errors (from drain_streams_concurrently), the barrier
    # is already stopped inside that method — stopping again is a no-op.
    @async_barrier&.stop
    raise
  end
end