Originally published at Learnetto.com.
When you start using React with Rails, one of the frustrating problems you quickly run into is trying to access associated model data from your React component.
Let's say we are building a chat app with Rails and React. We have three models, Chatroom, Message and User, which have the following relationships between them:
class Chatroom < ApplicationRecord
has_many :messages, dependent: :destroy
end
class Message < ApplicationRecord
belongs_to :chatroom
belongs_to :user
end
class User < ApplicationRecord
has_many :messages, dependent: :destroy
end
And we have a Message component for rendering individual messages:
import React from 'react'
const Message = ({message}) =>
{message.user.name}: {message.body}
export default Message
In addition to the message body we also want to display the user's name.
If you're using a standard Rails ERB or Haml view template, you can simply write something like:
<%= @message.user.name %>
And that will just work even if you didn't include the user data in your database query in the controller.
However, unlike in a Rails view, if we call message.user.name
 inside our React component without having specifically included that data in the prop sent to the component, it will throw an error.Â
While a Rails template is actually able to call the model on-the-fly and get data it doesn't have, we don't have that luxury with React.
We need to explicitly compose the JSON with the associated User model data because a React component can only access the JSON data we provide it.
There are many ways to include the associated model data, including manually composing the JSON in the controller, defining a custom as_json method on the model or using ActiveModelSerializers.
One of the cleanest and most flexible ways is to use jbuilder, which gives you a simple Domain Specific Language (DSL) for declaring JSON structures. The jbuilder gem comes included with Rails.
In the example above, we can include our user data in the message JSON by defining it in a _message.json.jbuilder file like this:
json.(message, :body, :id)
json.user do
json.extract! message.user, :id, :name, :image
end
The jbuilder DSL is quite powerful and allows you to do all sorts of custom structuring of the data.
For example, let's say we want to send all the messages in chronological order and user data for a particular chatroom to a Chatroom component. We can define the chatroom json like this:
json.(chatroom, :name, :id)
json.messages(chatroom.messages
.sort_by{|m| m[:created_at]}) do |message|
json.extract! message, :id, :body
json.user do
json.extract! message.user, :id, :name, :image
end
end
Check out the jbuilder documentation to learn more about all its features.
The full code of the Chat app is on Github and there's a live demo running here.
You can also learn how to build it yourself in this two-part video tutorial -  How to build a chat app with Rails 5.1 ActionCable and React.js Part 1 and Part 2.
Top comments (0)