Shaping State¶
When building .props
for your pages, it's useful to know that Superglue
believes the server generally drives UI state. If you worked with erb
files then you'll feel right at home.
Let's take a familiar approach to shaping state:
<header>
Email: <%= current_user.email %>
First Name: <%= current_user.first_name.capitalize %>
</header>
<div>
<h2>Post List</h2>
<%- @posts.each do |post| %>
<div>
<h3><%= post.title.capitalize %></h3>
<p><%= post.body %></p>
</div>
<% end %>
</div>
<div>
<h3>Menu</h3>
<%= link_to "Home", root_path%>
<div>
The .props
version of this is
json.header do
json.email current_user.email
json.firstName current_user.first_name
end
json.postList do
json.array! @posts do |post|
json.title post.title.capitalize
json.body post.body
end
end
json.menu do
json.homePath root_path
end
Note
Notice the convention of camelizing vs. non camelizing If you need to transform data, do it on the server side.
✅ Do it in ruby land
❌ Not in JS Land
Why UI state¶
Superglue encourages you to shape UI state on the server because
-
Ruby combined with Rails view heleprs is just as good, if not better, than Javascript at shaping state for React components.
-
UI patterns are universal - headers, footers, a body with a list of items exist in every app. Business logic varies wildly, but these presentational structures remain constant. This creates immediate intuition; a developer can easily guess the shape of the store, and make close-to-correct assumptions on how to update the store without looking at any code.
-
It's extremely productive with PropsTemplate. A keypath like props_at=data.content.barChart queries the server and grafts the response at the exact same location in your Redux state. This eliminates the traditional API dance - the path IS the query. Your component structure directly maps to your data fetching strategy.
View logic¶
Conventional Rails wisdom discourages excessive view logic, but .props
templates don't have the usual structural markup that you'd find with ERB templates. Its not uncommon to see more logic in your views.
Note
You can think of .props
as a presentational layer for your React components. We keep most of the UI state on the server the simplify the amount of Javascript we need.
daily_events = @events
.order(:starts_at)
.group_by do |event|
event.start_date
end
event_dates = daily_events.keys
json.rightMenu do
json.dailyLinks do
json.array! event_dates do |date|
json.text date.strftime("%A")
json.href "##{date}"
end
end
json.isChecked !!params[:mine_only]
json.newEventPath new_plan_event_path
end
json.dailyEvents do
json.array! event_dates do |date|
json.id date.to_s
json.header date.strftime("%A")
json.events do
json.array!(daily_events[date]) do |event|
json.starts_at event.starts_at
json.id event.id
json.name event.name
json.users event.users.map(&:full_name).to_sentence
json.editPath edit_event_path(event)
end
end
end
end
Deferment nodes¶
props_template
can selectively load parts of your JSON tree on demand using [digging] improving initial page load performance. To make the most of this feature, defer expensive operations until they're needed.
❌ Instead of
num_of_foo = 3.tap {sleep 4}
num_of_bar = 3.tap {sleep 1}
json.foo do
json.amount num_of_foo
end
json.bar do
json.bar num_of_bar
end
✅ Do
json.foo do
num_of_foo = 3.tap {sleep 4}
json.amount num_of_foo
end
json.bar do
num_of_bar = 3.tap {sleep 1}
json.bar num_of_bar
end
This allows you to make use of digging which allows you to skip the execution of longer running blocks.
For example: