Ruby on Rails 5.1+ & React: Working Example

Ruby on Rails 5.1+ & React: Working Example

Following the Ruby on Rails & React: The Easy Way tutorial, I’ve put a bit of that in practice to get a minimal working demo of a basic Rails application firing up while running the React-Rails and Webpacker gems. I’ll also show you how the default configuration magically works out in your favour so you don’t have to waste your time setting up Webpack to run the Babel Loader and deal with some arbitrary entry point to your entire project.

In this example, I’ll be building a countdown timer for New Year’s Day so you can ring in 2017 feeling like a million bucks. Obviously there is a much easier way of accomplishing this with just a static HTML page and some JavaScript. This is more of a primer to getting an otherwise complicated setup process running in minutes alongside your rails application. You should be able to apply these principles to any Rails app with ease and start developing with React, the way the social media gods (ahem.. Facebook) intended.

Setup

First, create your rails project

rails new ReactApp; cd ReactApp

In your Gemfile, add react-railswebpacker gems with

gem 'webpacker'
gem 'react-rails'

You will also need install these gems:

bundle install
rails webpacker:install
rails webpacker:install:react
rails generate react:install

We’re almost there. The last thing is to include the Webpacker javascript tag in your application template, app/view/layouts/application.html.erb. Here’s my entire application layout after this:

<!DOCTYPE html>
<html>
  <head>
    <title>ReactApp</title>
    <%= csrf_meta_tags %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag    'application' %>       <!-- This is the line we added -->
  </head>

  <body>
    <%= yield %>
  </body>
</html>

Creating Your Templates

At this time, we won’t worry about building an entire CRUD interface. The aim of this tutorial is to get ES6-based components working and successfully importing from NPM packages.

rails g controller static tester

While we’re here, we should probably root your app router to your tester template/action…

Change this…

Rails.application.routes.draw do
  get 'static/tester'

  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

To this…

Rails.application.routes.draw do
  root :to => 'static#tester'

  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

Create your first component. In this example, I’ll be using ES6. If you’re more comfortable with ES5 syntax, go for it. Just change the flag at the end of the next command to “--es5“.

rails g react:component Countdown greeting:string date:string --es6

Edit your tester.erb file and update it to something like the following. This is to include the React component we’ll be rendering and the props we’ll be passing into it. Handy eh?

<h1>Hi! I'm the Template!</h1>

<%= react_component("Countdown", {greeting: "New Year's Day", date: "20180101"}) %>

Check out your generated Countdown.js file. This is what it should look like, and it should be fine for now.

import React from "react"
import PropTypes from "prop-types"
class Countdown extends React.Component {
  render () {
    return (
      <div>
        <div>Greeting: {this.props.greeting}</div>
        <div>Date: {this.props.date}</div>
      </div>
    );
  }
}

Countdown.propTypes = {
  greeting: PropTypes.string,
  date: PropTypes.string
};
export default Countdown

Make It Work

At this point, you should be able to fire up your Rails server after getting Webpack to compile your changes to the single component you’ve just created. Open your terminal and hit it with this:

webpack
rails s

Here’s where we need to import our first NPM package. We’ll use Moment.js to give you a generalized number of days until the new year, and then give you a countdown in hours and minutes when that day arrives.

First, install Moment.js with npm.

npm install moment --save

Note: If you run into issues about files not existing or ‘lib/.bin/webpack’ not being found, try getting Yarn to fix your dependencies first. It’s just like including gems that you haven’t installed yet. yarn install should solve most of your problems.

Update Countdown.jsx to give it a couple of new functions. Basically, we’ll be updating it every second to display a new length of time relative to the “date” property you’ve supplied in the Rails template. This is great because we can reuse it throughout the application as I’ll show you shortly.

import React from "react"
import PropTypes from "prop-types"
import Moment from "moment"

class Countdown extends React.Component {

  constructor(props) {
    super(props);

    this.state = { time:  Moment(this.props.date, "YYYYMMDD").fromNow() }
  }

  tick () {
    this.setState( { time:  Moment(this.props.date, "YYYYMMDD").fromNow() } )
  }

  componentDidMount () {
    this.interval = setInterval(this.tick, 1000);
  }

  componentWillUnmount () {
    clearInterval( this.interval );
  }

  render () {

    return (
      <div>
        <h1>{ this.props.greeting } is { this.state.time }</h1>
      </div>
    );
  }
}

Countdown.propTypes = {
  greeting: PropTypes.string,
  date: PropTypes.string
};
export default Countdown

At this point, we shouldn’t have to touch our ERB file unless you want to make it look prettier. For brevity, I’ll skip that part and leave it to you – the one with an eye for design ;).

Let’s fire up Webpack to see how we did. Note: -w watches the file for changes and recompiles all necessary js when it needs to. -d is for “Development Mode” and allows you to see your ES6 errors directly in the browser’s console, rather than errors coming from your compiled version. Let me know in the comments if you’re having any other issues.

webpack -w -d

If everything went well, we should see no errors pop up.

Finishing Touches

Obviously we can make this look prettier. The entire point of this tutorial was to get a React component running in a Rails application and demonstrate usage with npm packages.

One thing that makes this very nice and clean to use in templates is the ability to reuse the components you’ve built without being intrusive to your Rails application. This is great if you’re retrofitting React to your Rails app.

Reusing components is easy. Have a look at this updated tester.html.erb

<h1>Look at all these dates!</h1>

<%= react_component("Countdown", {greeting: "New Year's Day",  date: "20180101"}) %>
<%= react_component("Countdown", {greeting: "Ireland Trip",    date: "20180212"}) %>
<%= react_component("Countdown", {greeting: "Valentine's Day", date: "20180214"}) %>
<%= react_component("Countdown", {greeting: "My Birthday",     date: "20180509"}) %>

Further Reading

Here are a few links related to things I talked about in this tutorial: