Class: ReactOnRailsPro::AsyncPropsEmitter

Inherits:
Object
  • Object
show all
Defined in:
lib/react_on_rails_pro/async_props_emitter.rb

Overview

Emitter class for sending async props incrementally during streaming render. Used by stream_react_component_with_async_props helper.

PROTOCOL: Each call to ‘emit.call(prop_name, value)` sends an NDJSON line to the Node renderer:

{"bundleTimestamp": "abc123", "updateChunk": "(function(){...})()"}

The updateChunk JavaScript accesses the AsyncPropsManager via sharedExecutionContext and resolves the promise for that prop, allowing React to continue rendering.

WHY NOT USE GLOBAL VARIABLES? Global variables in Node.js VM persist across requests, causing data leakage. sharedExecutionContext is scoped to a single HTTP request (ExecutionContext).

Examples:

Usage in view

stream_react_component_with_async_props("Dashboard") do |emit|
  emit.call("users", User.all.to_a)      # Sends immediately
  emit.call("posts", Post.recent.to_a)   # Sends when ready
end

Instance Method Summary collapse

Constructor Details

#initialize(bundle_timestamp, request_stream) ⇒ AsyncPropsEmitter

Returns a new instance of AsyncPropsEmitter.



24
25
26
27
# File 'lib/react_on_rails_pro/async_props_emitter.rb', line 24

def initialize(bundle_timestamp, request_stream)
  @bundle_timestamp = bundle_timestamp
  @request_stream = request_stream
end

Instance Method Details

#call(prop_name, prop_value) ⇒ Object

Sends an async prop to the Node renderer. The prop value is JSON-serialized and sent as an NDJSON line. On the Node side, this triggers asyncPropsManager.setProp(propName, value).



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/react_on_rails_pro/async_props_emitter.rb', line 32

def call(prop_name, prop_value)
  update_chunk = generate_update_chunk(prop_name, prop_value)
  @request_stream << "#{update_chunk.to_json}\n"
rescue StandardError => e
  Rails.logger.error do
    backtrace = e.backtrace&.first(5)&.join("\n")
    "[ReactOnRailsPro::AsyncProps] Failed to send async prop '#{prop_name}': " \
      "#{e.class} - #{e.message}\n#{backtrace}"
  end
  # Continue - don't abort entire render because one prop failed
end

#end_stream_chunkObject

Generates the chunk that should be executed when the request stream closes This tells the asyncPropsManager to end the stream



46
47
48
49
50
51
# File 'lib/react_on_rails_pro/async_props_emitter.rb', line 46

def end_stream_chunk
  {
    bundleTimestamp: @bundle_timestamp,
    updateChunk: generate_end_stream_js
  }
end