johnthethird
2/2/2014 - 7:01 PM

React Server-Side Rendering for Rails

React Server-Side Rendering for Rails

require 'connection_pool'

class ReactRenderer

  mattr_accessor :pool

  def self.setup!
    size = ::Rails.configuration.react.max_renderers || 10
    timeout = ::Rails.configuration.react.renderer_timeout || 20 #seconds
    @@pool ||= ConnectionPool.new(:size => size, :timeout => timeout) { ReactRenderer.new }
  end

  def self.render(component, args={})
    @@pool.with do |renderer|
      renderer.render(component, args)
    end
  end

  def context
    @context ||= begin
      react_code = File.read(::React::Source.bundled_path_for("react-with-addons.min.js"))
      components_code = ::Rails.application.assets['components.js'].to_s
      all_code = <<-CODE
        var global = global || this;
        #{react_code};
        React = global.React;
        #{components_code};
      CODE
      ExecJS.compile(all_code)
    end
  end

  def render(component, args={})
    # This works because even though renderComponentToString uses a callback API it is really synchronous
    jscode = <<-JS
      function() {
        var html = "";
        React.renderComponentToString(#{component}(#{args.to_json}), function(s){html = s});
        return html;
      }()
    JS
    context.eval(jscode).html_safe
  end

end