Give Codeship’s CI/CD Platform a Try

Want to learn more?

I have never felt as productive as I do in Rails. Yet, with front-end seemingly moving further away from server-rendered views toward React, Angular, Vue, and Ember, I was unsure where Rails fit into this picture. Would it be relegated only to apps with “simple” front-ends, where holding things together with jQuery still managed to work, or perhaps to its new API mode, serving up data via RESTful JSON feeds to be consumed by client-side code and mobile apps?

The answer to that question may be yes, because Rails still does an amazing job at that. But perhaps the answer lies more in the realm of “maybe…it depends”. Would it be better to bring the best of the front-end and have it live happily within the Rails ecosystem?

In this article, Marian Serna and I will explore different ways of using React inside of your new or existing Rails apps.

Rails Embraces JavaScript

JavaScript has its flaws and that’s why Rails embraced CoffeeScript early on instead of plain JS. With ES6 and Babel, CoffeeScript seemed less and less necessary, and in fact to me became something I disliked writing when most examples and articles online were using ES6 syntax. The js2coffee website came in handy to convert between the two syntaxes.

Then came the issue of how to include JS libraries in your Rails app. The typical way would be to look for a “Rails” version of the JS library on rubygems.org, or perhaps take advantage of rails-assets.org. It was a good solution for the time, but to fully embrace JS and provide a more seamless integration, we needed the ability to have a package.json file to list our JS dependencies, install them via yarn, and then package them together with a tool like webpack.

With Rails 5.1, it feels like we finally have a great JS story rather than one that is just passable. It comes with support for yarn, es6, and webpack. Read here for a great intro to exactly what this includes. Also, though it’s a paid course, I recommend the ES6 course by Wes Bos as a great introduction to the new features of JS.

Rails Embraces React

React on Rails is a gem that allows you to organize and use React inside of a Rails project, easily rendering components (and passing them data) from within your Rails views. Additionally, it allows you to take advantage of NPM JavaScript libraries with yarn.

Although Rails 5.1 gives you some of this ability, React on Rails presents a nice alternative for people who want the best of the front-end within Rails. It creates a structured and complete solution where any front-end developer, who may not have any prior Rails experience, can jump in and feel at home.

We want to install it with Redux: rails generate react_on_rails:install --redux --node

This will prepare your Rails application to be able to render React components by creating a client folder where all of your React work will take place. Additionally, it adds a ‘HelloWorld’ example that includes a controller, route, view, and a component. Use git diff to see which files have been added and/or modified.

One caveat we’ve noticed with Rails 5.1 is that since it comes with a package.json file of its own, there may be a conflict and you will have to manually merge them together.

Inside of the client folder, you will find a fully separate React setup that comes with its own package.json file, containing all of the NPM modules necessary to run React with Webpack and ES6. Webpack comes preconfigured. All of our components will live inside of client/app/bundles.

Our First Component

You can use as much or as little of React as you want in your Rails app. It doesn’t need to take over the entire front-end. Here is an example using React to create a hamburger nav menu; one small piece of a larger page.

Inside of the application layout file, a React component is rendered using the react_component helper method provided by react_on_rails.

Visiting any page on www.marianserna.com, you’ll see the hamburger menu on the top-right hand corner of the screen.

Passing Data from Rails to React

In React, components generally have two types of “data” to worry about: Props and State. Props are read-only data passed from a parent component to a child. State is data internal to and managed by a specific component. If you need state, that is up to you, but how you pass props from your Rails view to your React component is done like so:

Posting Data Back to Rails

When you need to send data back to Rails, you have quite a few different options. You could use jQuery.post, fetch (remember to include a polyfill for older browser support), or a library like axios. These are just standard AJAX requests like you’re used to. In this case, we’re treating our Rails app like an API, usually expecting JSON as the response.

In a slightly more in-depth react_on_rails integration, this is how you might post data to create a House object that has many related Image records. This takes advantage of Rails’ accepts_nested_attributes_for ability to create parent and child records at the same time. The code is available on GitHub. Refer to the repo and the controllers/models/serializers to see how the Rails side of this example was done.

One thing you might have noticed is the ability to include the CSRF token in our POST request to Rails. This is functionality provided by react_on_rails and must be imported at the top of your file: import ReactOnRails from 'react-on-rails';. The complete file can be found here.

!Sign up for a free Codeship Account

Managing State in Redux

As your app grows in complexity, it may be a good idea to avoid managing state in each of your components (or a single Main/Parent component) and instead manage it using Redux. The talented Wes Bos again has a course for learning this, which happens to be free!

react_on_rails comes with built-in support for Redux and all of the packages are included if you install it with the --redux option.

Redux is very verbose and in my opinion sort of complicated to get set up. Thankfully the HelloWorld example of react_on_rails contains a complete setup. What you’ll see are folders for the following items:

store: This is where you will define your Redux data store (or global state). You can include any middleware you may want, like thunk, which we’ll touch on very briefly below.

actions: Actions in redux are functions which are in charge of preparing data and talking with external resources before finally dispatching an event with the goal of updating the store’s state. Your business logic lives here. These work hand in hand with reducers which we’ll talk about below.

constants: These are constants so that the actions and reducers can refer to the same events. It is how the action tells the reducer which data is going to be changed.

reducers: Reducers have the task of taking the existing copy of the store’s state and producing a new version of it based on the data dispatched/returned from an action.

containers: This is the glue between a Component and the Redux store + actions. They are small wrappers which pass the Redux store as props to the component. Any time a store’s data is updated, the component will receive those updates via props to force a rerender.

Without going into the full complexity of Redux, we’ll try to show a simple example below that fetches the information for a single House object in our demo app.

When the HouseInfo component mounts, it will make a call to the action, telling it which house it needs data for.

The loadHouse action lives inside of a file which contains all related actions. Because this AJAX request happens asynchronously, our loadHouse will return a function that will get passed a dispatch function. dispatch can be called any time we have more information we want to update in the store (via a reducer).

For more information on dealing with asynchronous code inside of Redux, check out the redux-thunk library.

Client Side Routing in React

If we want routing to be handled client-side using React Router, this isn’t a problem with react_on_rails. The trick to make it work is that you’ll essentially have to handle routing twice…once inside of Rails (which will render the component) and then again inside of React.

The Rails routes are pretty standard:

Rails.application.routes.draw do
root to: "houses#index"
resources :houses, only: [:new, :show, :create] do
collection do
get :featured
get :search
end
end
end

What you don’t see here is that houses#index, houses#show, and houses#new all render the same component:

= react_component("HomeApp", props: {}, prerender: false)

This HomeApp component then takes over and figures out the routing on the client side.

One thing that caused some confusion was not having Switch wrapping the routes…it was causing ambiguous routing, thinking that the “new” part of "/houses/new" was actually an ID of a house. Switch deals with this by sticking with the route that matches correctly first, so the order you list them in is important.

A Word on Authentication

Because authentication in this demo app was done via Devise, we stuck with using the typical cookie-based user session.

As you move more and more toward your entire app living in React, or even supporting ReactNative or other native apps, you might be better off not assuming that cookies will exist and instead looking into JSON Web Tokens. This is beyond the scope of the article though, so will have to be explored at a later date.

Conclusion

We hope we were able to show that including React inside of your Rails app is not only possible, it is a fairly elegant solution to including more front-end interactivity and avoiding the spaghetti code that jQuery can often end up turning into.

It’s up to you how much or how little to include. It can be as small as a nav component on a specific page, it can be an entire page of your app, or you can use Redux + React Router to take over larger and larger areas of your application.

Don’t feel the need to abandon server-rendered code. It can still be a joy to write and is often the most simple solution. At the same time, it’s great being able to take advantage of all the cutting edge client-side code available these days.

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles. Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.

We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.