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, content_type: nil, **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).

  • content_type (String, nil) (defaults to: nil)

    Optional response content type. Set after rendering but before the first stream write, overriding any content type inferred from the template format. When using a non-HTML ‘formats:` value (for example `[:text]`), pass `content_type` too unless committing the format-derived MIME type is intentional.

  • render_options (Hash)

    Additional options to pass to ‘render_to_string`.

See Also:

  • ReactOnRails::Helper#stream_react_component


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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/react_on_rails_pro/concerns/stream.rb', line 39

def stream_view_containing_react_components(
  template:, close_stream_at_end: true, content_type: nil, **render_options
)
  require "async"
  require "async/barrier"
  require "async/limited_queue"
  warn_on_non_html_formats_without_content_type(render_options[:formats], content_type)

  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
    # `formats: [:text]` causes render_to_string to set response.content_type
    # to `text/plain`; override it here before the first stream write, which
    # is when ActionController::Live commits headers. render_to_string itself
    # never writes to response.stream, so this assignment is always safe.
    response.content_type = content_type if content_type
    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