diff --git a/lib/assets/javascripts/react_ujs.js.erb b/lib/assets/javascripts/react_ujs.js.erb index 7d1262c8d..efe0c4ee6 100644 --- a/lib/assets/javascripts/react_ujs.js.erb +++ b/lib/assets/javascripts/react_ujs.js.erb @@ -9,6 +9,7 @@ window.ReactRailsUJS = { CLASS_NAME_ATTR: 'data-react-class', PROPS_ATTR: 'data-react-props', + PROPS_ID_ATTR: 'data-react-props-id', RAILS_ENV_DEVELOPMENT: <%= Rails.env == "development" %>, // helper method for the mount and unmount methods to find the // `data-react-class` DOM elements @@ -27,13 +28,22 @@ var nodes = window.ReactRailsUJS.findDOMNodes(); for (var i = 0; i < nodes.length; ++i) { + var propsId, propsElement, propsJson; var node = nodes[i]; var className = node.getAttribute(window.ReactRailsUJS.CLASS_NAME_ATTR); // Assume className is simple and can be found at top-level (window). // Fallback to eval to handle cases like 'My.React.ComponentName'. var constructor = window[className] || eval.call(window, className); - var propsJson = node.getAttribute(window.ReactRailsUJS.PROPS_ATTR); + + propsId = node.getAttribute(window.ReactRailsUJS.PROPS_ID_ATTR); + if (propsId != null) { + propsElement = document.getElementById(propsId); + propsJson = propsElement && propsElement.text; + } else { + propsJson = node.getAttribute(window.ReactRailsUJS.PROPS_ATTR); + } + var props = propsJson && JSON.parse(propsJson); React.render(React.createElement(constructor, props), node); diff --git a/lib/react/rails/railtie.rb b/lib/react/rails/railtie.rb index 3ea4ae743..c8d00916e 100644 --- a/lib/react/rails/railtie.rb +++ b/lib/react/rails/railtie.rb @@ -9,6 +9,7 @@ class Railtie < ::Rails::Railtie config.react.variant = (::Rails.env.production? ? :production : :development) config.react.addons = false config.react.jsx_transform_options = {} + # Server-side rendering config.react.max_renderers = 10 config.react.timeout = 20 #seconds diff --git a/lib/react/rails/view_helper.rb b/lib/react/rails/view_helper.rb index 5abb55e7a..4d242c598 100644 --- a/lib/react/rails/view_helper.rb +++ b/lib/react/rails/view_helper.rb @@ -9,18 +9,49 @@ module ViewHelper def react_component(name, args = {}, options = {}, &block) options = {:tag => options} if options.is_a?(Symbol) block = Proc.new{concat React::Renderer.render(name, args)} if options[:prerender] + separate_props = options.delete :separate_props + move_separate_props_out = options.delete :move_separate_props_out html_options = options.reverse_merge(:data => {}) html_options[:data].tap do |data| data[:react_class] = name - data[:react_props] = React::Renderer.react_props(args) unless args.empty? + next if args.empty? + if separate_props + data[:react_props_id] = add_react_props args, move_separate_props_out + else + data[:react_props] = React::Renderer.react_props args + end end html_tag = html_options[:tag] || :div # remove internally used properties so they aren't rendered to DOM html_options.except!(:tag, :prerender) - - content_tag(html_tag, '', html_options, &block) + + result = content_tag(html_tag, '', html_options, &block) + result += render_react_props html_options[:data][:react_props_id] if separate_props && !move_separate_props_out + result + end + + # Add properties for component and return element id. + # + def add_react_props(props={}, move_out=false) + return if props.empty? + props_id = SecureRandom.base64 + content_key = "react_props" + content_key += "_#{props_id}" unless move_out + content_for content_key do + content_tag :script, type: 'text/json', id: props_id do + raw React::Renderer.react_props props + end + end + props_id + end + + # Render script tag with JSON props. Should be placed at the end of body + # in order to speedup page rendering. + # + def render_react_props(element_id=nil) + element_id.nil? && content_for('react_props') || content_for("react_props_#{element_id}") end end diff --git a/test/dummy/app/controllers/separate_controller.rb b/test/dummy/app/controllers/separate_controller.rb new file mode 100644 index 000000000..819d5ba64 --- /dev/null +++ b/test/dummy/app/controllers/separate_controller.rb @@ -0,0 +1,2 @@ +class SeparateController < ServerController +end diff --git a/test/dummy/app/views/layouts/application.html.erb b/test/dummy/app/views/layouts/application.html.erb index 670d1875b..4a52a7a76 100644 --- a/test/dummy/app/views/layouts/application.html.erb +++ b/test/dummy/app/views/layouts/application.html.erb @@ -10,5 +10,9 @@ <%= yield %> +
+ +<%= render_react_props %> +