-
-
Notifications
You must be signed in to change notification settings - Fork 631
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change div tags to script tags for props #411
Changes from all commits
5ca16db
d452721
f9331b6
6ca0030
aab243c
5ad21af
ff0c968
edd0ed0
7c892d7
0663cee
f81fb59
64a7ced
bebf370
afeea39
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,3 +39,4 @@ gem "chromedriver-helper" | |
gem "launchy" | ||
gem "poltergeist" | ||
gem "selenium-webdriver" | ||
gem "yajl-ruby" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
# 2. Keep all #{some_var} fully to the left so that all indentation is done evenly in that var | ||
require "react_on_rails/prerender_error" | ||
require "addressable/uri" | ||
require "yajl" | ||
|
||
module ReactOnRailsHelper | ||
# The env_javascript_include_tag and env_stylesheet_link_tag support the usage of a webpack | ||
|
@@ -88,6 +89,7 @@ def env_stylesheet_link_tag(args = {}) | |
# raise_on_prerender_error: <true/false> Default to false. True will raise exception on server | ||
# if the JS code throws | ||
# Any other options are passed to the content tag, including the id. | ||
# rubocop:disable Metrics/AbcSize | ||
def react_component(component_name, raw_options = {}) | ||
# Create the JavaScript and HTML to allow either client or server rendering of the | ||
# react_component. | ||
|
@@ -102,11 +104,13 @@ def react_component(component_name, raw_options = {}) | |
# The reason is that React is smart about not doing extra work if the server rendering did its job. | ||
|
||
component_specification_tag = | ||
content_tag(:div, | ||
"", | ||
content_tag(:script, | ||
class: "js-react-on-rails-component", | ||
style: options.style, | ||
data: options.data) | ||
style: nil, | ||
data: options.data) do | ||
props = Yajl.dump(options.props.is_a?(String) ? JSON.parse(options.props) : options.props) | ||
"var #{options.dom_id.tr('-', '_')} = #{props};".html_safe | ||
end | ||
|
||
# Create the HTML rendering part | ||
result = server_rendered_react_component_html(options.props, options.name, options.dom_id, | ||
|
@@ -126,12 +130,12 @@ def react_component(component_name, raw_options = {}) | |
|
||
# IMPORTANT: Ensure that we mark string as html_safe to avoid escaping. | ||
result = <<-HTML.html_safe | ||
#{component_specification_tag} | ||
#{component_specification_tag} | ||
#{rendered_output} | ||
#{options.replay_console ? console_script : ''} | ||
HTML | ||
|
||
prepend_render_rails_context(result) | ||
add_render_rails_context(result) | ||
end | ||
|
||
# Separate initialization of store from react_component allows multiple react_component calls to | ||
|
@@ -154,7 +158,7 @@ def redux_store(store_name, props: {}, defer: false) | |
@registered_stores ||= [] | ||
@registered_stores << redux_store_data | ||
result = render_redux_store_data(redux_store_data) | ||
prepend_render_rails_context(result) | ||
add_render_rails_context(result) | ||
end | ||
end | ||
|
||
|
@@ -222,31 +226,31 @@ def server_render_js(js_expression, options = {}) | |
|
||
private | ||
|
||
# prepend the rails_context if not yet applied | ||
def prepend_render_rails_context(render_value) | ||
# add the rails_context if not yet applied | ||
def add_render_rails_context(render_value) | ||
return render_value if @rendered_rails_context | ||
|
||
data = { | ||
rails_context: rails_context(server_side: false) | ||
} | ||
|
||
@rendered_rails_context = true | ||
|
||
rails_context_content = content_tag(:div, | ||
"", | ||
rails_context_content = content_tag(:script, | ||
id: "js-react-on-rails-context", | ||
style: ReactOnRails.configuration.skip_display_none ? nil : "display:none", | ||
data: data) | ||
"#{rails_context_content}\n#{render_value}".html_safe | ||
style: nil) do | ||
"var #{'js-react-on-rails-context'.tr('-', '_')} = #{Yajl.dump(rails_context(server_side: false))};".html_safe | ||
end | ||
"#{render_value}\n#{rails_context_content}".html_safe | ||
end | ||
|
||
def render_redux_store_data(redux_store_data) | ||
result = content_tag(:div, | ||
result = content_tag(:script, | ||
"", | ||
class: "js-react-on-rails-store", | ||
style: ReactOnRails.configuration.skip_display_none ? nil : "display:none", | ||
data: redux_store_data) | ||
prepend_render_rails_context(result) | ||
style: nil, | ||
data: redux_store_data) do | ||
# redux_store_data | ||
"var #{redux_store_data[:store_name].tr('-', '_')} = #{Yajl.dump(redux_store_data[:props])};".html_safe | ||
end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generating a JavaScript string from Ruby is a pretty bad idea in my experience. For one thing the generated identifier is in the global namespace, but it’s also potentially very fragile. My personal preference for getting values from Ruby into JS is to provide them as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Global namespace shouldn't be a problem. Attempt to make it as unique as possible though. Prepend it with a bunch of underscores or something similar. I'd advise against using a data attribute though: You might accidentally include an ambiguous ampersand which is not allowed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kapowaz a possible reason behind this is that initial props can be quite big, e.g. 100Kb. It is really awkward and unreadable to cram it to HTML attribute, given that every quote is replaced with |
||
|
||
add_render_rails_context(result) | ||
end | ||
|
||
def props_string(props) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest not playing games with global Javascript variables but instead use
Also, it would really help development if you use human-readable JSON in non-development environment, a-la:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also looks like JSON
<script>
tag also solves the Turbolinks issue, because there is no need to execute any<script>
tag, you just have to replaceel.getAttribute()
with (IIRC).textContent
. Also, noeval()
!