React and Rails: How to render each Rails item as a React Component

This post is about going from using HTML with embedded ruby to just loop through a bunch of items and render their HTML, to removing that block of HTML into a React component and rendering one such component for every object in the array.

Luckily the React-rails gem (https://github.com/reactjs/react-rails) is the bees knees and has a built-in method called react_component that makes this quite easy to do.

In the interest of brevity I’ll take it from the point where we have React installed and working (the readme for react-rails repo is very good) and now we want to render a component for each item.

For this side project (a Daycare finder where you can search and filter for daycares in your areas), I have a model called DayCare and a controller that is called daycarecontroller.rd and the controller has this method:

def index
   @day_cares = DayCare.all
end

(This is just normal Rails without any React).

Then, in app/views/day_cares/index.html.erb, I was outputting the HTML in my index.html.erb like so:

<% @day_cares.each do |day_care| %>
<div class=“row”>
    <div class=“col-xs-12”>
        <div class=“row”>
             <div class=“col-xs-12 col-sm-6”>
                   <div class=“streetview-img”></div>
                   <h3 class=“daycare-name”><%=     day_care.name %></h3>
            </div>
            <div class=“col-xs-12 col-sm-6”>
                 <h6 class=“street-address”><%= day_care.street %>, <%= day_care.city %></h6>
                 <p><%= day_care.description %></p>
            </div>
        </div>    
    </div>
</div>
<% end %>

(Personally I find it’s a lot easier to code the ‘component’ in HTML.erb like this and only then turn each block into an actual React component, as opposed to getting it all to work at once).

Now I want move all this HTML into React to take advantage of the amazing “one-way reactive data flow”, which is Facebookish for “make the component re-render itself on state change”. Handy, because these day care listing need to respond to search and filter actions.

We need a parent component, and then for each object passed is as props, the parent component will render a Daycare child component.

So, I replaced the whole block of code above (the Rails .erb way) with the React way, using the built in react_component method:

<%= react_component ‘DayCareListings’, { data: @day_cares } %>

DayCareListings is the name of the parent component, which is what I want to render, and I want to pass it all the info as props inside the {} - @day_cares how the controller defines the data.

The parent component for now just renders child components. Here is my basic parent component:

var DayCareListings = React.createClass({
    render: function() {
        return (
            <div>
                < Daycare />
            </div>
         )
    }
});

The HTML that I used to have as erb is is now moved to the render method of my child component called DayCare (where it is now technically JSX):

var Daycare = React.createClass({
    render: function() {
    return (
        <div class=“row”>
            <div class=“col-xs-12”>
                 <div class=“row”>
                      <div class=“col-xs-12 col-sm-6”>
                           <div class=“streetview-img”></div>
                           <h3 class=“daycare-name”>The Name</h3>
                      </div>
                      <div class=“col-xs-12 col-sm-6”>
                          <h6 class=“street-address”>The street address,the city</h6>
                          <p>the description</p>
                     </div>
                 </div>    
             </div>
          </div>
        )
    }
});

At which point by browser gently reminds me that I’m being a toss and “class” is not a thing in JSX.

Why thank you browser. Yes, I meant className. So lets change that.

As you can see in the react component above, there is no data. What in erb was day_care.name and so on, is now being called The Name, because it’s easier for me to do things incrementally.

The data is available though - by inspecting the element being rendered I can see that the component has all the data from my Rails model as prop, where I have an of objects - Yay!

And the component has been put into a container automatically, a div appropriately called DayCareListings. Thanks React!

image 2

The next step is to use this data to get the parent component for render one child component for every object in the day care array.

Which was previously being done in my erb with

<% @day_cares.each do |day_care| %>

Lets get the parent component to render one child component for each daycare object in that array and pass it the data as props.

Updated parent object:

var DayCareListings = React.createClass({
    render: function () {
        var dayCareNodes =    this.props.data.map(function (daycare, index) {
       return (
       <Daycare name={daycare.name} description={daycare.description} street={daycare.street}  city={daycare.city} key={index} />
       );
   });
   return (
         <div className=“DayCareList”>
           {dayCareNodes}
         </div>
     );
    }
});

And then in the child component, simply replace “The Name” with {this.props.name}, and so on, because the props are now being passed in.

var Daycare = React.createClass({
    render: function() {
        return (
            <div className=“row”>
                <div className=“col-xs-12”>
                    <div className=“row”>
                        <div className=“col-xs-12 col-sm-6”>
                    <div className=“streetview-img”></div>
                      <h3 className=“daycare-name”>{this.props.name}</h3>
                  </div>
                   <div className=“col-xs-12 col-sm-6”>    
                    <h6 className=“street-address”>{this.props.street},{this.props.city}</h6>
                     <p>{this.props.description} </p>
                     </div>
                  </div>    
               </div>
            </div>
        )
    }
});

It works! Now I have 5 daycare components being rendered on the page with their actual names, addresses and descriptions.

Looking back at
app/views/day_cares/index.html.erb,

we now have gone from this:

<% @day_cares.each do |day_care| %>
// A bunch of HTML with embedded ruby
<% end %>

to this:

<%= react_component 'DayCareListings’, { data: @day_cares } %>

Jolly good.