Use React Today in Ruby by Heading to Cuba

Share this article

Use React Today in Ruby by Heading to Cuba

react

You’ve all heard of React. It’s no longer the new kid on the block and has achieved enough of a foothold in the volatile Javascript ecosystem that we’re not concerned about learning something that will be redundant in 6 months. React is based on some very good ideas; It replaces the View in the Model-View-Controller pattern with a virtual Document Object Model and a blazingly fast diffing algorithm that ensures the only updates to the page are the changes you have made. The only downside is having to write JSX or Javascript! Fortunately, we can get around that with some Ruby APIs designed to solve exactly that problem.

Is React Right For Your App?

React shines when used with Single Page Apps or in any instance where partial-page reload is beneficial. Rails has had Turbolinks (think PJAX if you’re not familiar) out of the box since 4.0, so if you’re looking for speed via partial re-render, you’re likely better off with that. React also doesn’t offer you any AJAX functionality, a data layer, or any of the other things we take for granted in modern web development. So, if you think of it as drop-in replacement for Rails, you’re going to have a bad time.

Also, since it’s predominantly designed for dynamic page re-rendering, it’s going to be a large investment for small return if you have a mostly static site. Oh, and if you’re supporting browsers lower than IE9, then this is not for you because React doesn’t work with them and never will. In short and like anything, if you don’t play to the strengths of the tool you are using, then you’re probably better off not using it.

With that out of the way, let’s get started!

Integration

You want to integrate this into your Rails project? No problem! The Rails ecosystem has several methods of integration including WebPack, Gulp, and a few plugins (react-rails appears to be leading the pack at the time of writing) that do exactly that. What if you’re not using Rails? What if you prefer the lightness afforded to you by a micro-framework like Cuba or Sinatra? Well, then we have a little more work to do.

First, start a new skeleton project. We’ll use Cuba for this, but it will work equally well with Sinatra (with some slight modifications to your config.ru). For quick and dirty projects, I use the excellent Cuba Genie, which bundles Bootstrap, Cuba, and a bunch of nice touches into a simple project.

Assuming you have a simple project working, here’s how you integrate React:

Add the following Gems to your Gemfile:

gem "opal-jquery"
gem "reactive-ruby"

and run bundle install

First, create some simple HTML templates. I’m using mote:

<!-- views/layout.mote -->
<!doctype html>
<html>
  <head>
    <script src="/assets/react.js"></script>
    <script>{{ app.loader }}</script>
  </head>
  <body>
    <div id="layout">
        {{ content }}
    </div>
  </body>
</html>

<!-- home.mote -->
<div id='content'></div>

There’s a couple of things to talk about here: First off, the inclusion of <div id="content">. Those of you who’ve glanced at the React tutorial might recall that React needs a mount point on the DOM. This is that.

/assets/react.js – This file doesn’t exist on our file system; it is transpiled at runtime from react.rb, which we will look at in a moment.

{{ app.loader }} will translate to this string:

if (typeof(Opal) !== 'undefined') {
  Opal.mark_as_loaded("opal");
  Opal.mark_as_loaded("corelib/runtime.self");
  Opal.mark_as_loaded("jquery.self");
  Opal.mark_as_loaded("sources/react.self");
  Opal.load("react");
}

This is handling loading third-party libraries, including React and jQuery while allowing the transpiler to serve the JS generated from our Ruby code. It doesn’t work without this string.

Now let’s create a simple React class. In spite of the file type, this will be located in your js directory:

# react.rb:
require 'opal'
require 'jquery'
require 'opal-jquery'
require 'reactive-ruby'

class HelloWorld
  include React::Component
  def render
    h1 {"Hello, World!"}
  end
end

This is a simple div with the h1 text, Hello, World. It is the equivalent of the following JSX:

ReactDOM.render(<h1>Hello World</h1>, document.getElementById('content'));

We’ll use jQuery’s document.ready event to mount React. This necessitates adding jQuery to your project:

Document.ready? do
  React.render(React.create_element(HelloWorld), Element["#content"])
end

Next, we need to add the loader method to our Cuba instance. This can be done by adding this code to your Cuba class:

# app.rb
require 'opal'
def loader
  opal = Opal::Server.new {|s|
    s.append_path 'js'
    s.main = 'react'
  }
  Opal::Processor.load_asset_code(opal.sprockets, 'react')
end

Finally, none of this will work without a few modifications to our config.ru: we need to serve our assets, set some template variables, and gear our app up for transpiling. Your config.ru should look like this:

# config.ru
require 'bundler'
require_relative 'app'

Bundler.require

opal = Opal::Server.new {|s|
  s.append_path 'js'
  s.main = 'app'
}

sprockets   = opal.sprockets
maps_prefix = '/__OPAL_SOURCE_MAPS__'
maps_app    = Opal::SourceMapServer.new(sprockets, maps_prefix)

# Monkeypatch sourcemap header support into sprockets
::Opal::Sprockets::SourceMapHeaderPatch.inject!(maps_prefix)

map maps_prefix do
  run maps_app
end

map '/assets' do
  run sprockets
end

run(Cuba)

This starts an Opal server, sets the entry points for the transpiler, injects header support into sprockets, and serves all of this from the assets directory.

The moment of truth: navigate to 127.0.0.1:9292 to see:

`Hello, World`

Congratulations; you are now running the cutting edge of Javascript technology in a microframework!

Notes

This is a surprisingly large amount of work to integrate something into your project. We’ve had to configure transpilers, set mount points, define entry points, write HTML. and all for “Hello World!”. I quickly tired of this and wrote a Gem, called cuba_react to do all of this for you. I would advocate using the gem rather than spending hours in Pry’s debug console fervently trying to ascertain for what reason your transpiler isn’t seeing source files or not returning what you think it is.

More importantly, the gem will automate all of this and allow you to skip the boring stuff and just get started playing with React! Add cuba_react to your Gemfile, bundle install, and leverage the inbuilt CLI to generate all of these files for you with cuba_react generate. Documentation can be found here.

Extending Our Example

Naturally, “Hello, World!” is just about useless in the real world, so you should extend our example a little. Check out the excellent reactrb docs for some examples of what’s possible, but here’s a simple login class:

class Nav < React::Component::Base
  def render
    div do
      input(class: :handle, type: :text, placeholder: "Enter Your Handle")
      button(type: :button) { "login!" }.on(:click) do
        alert("#{Element['input.handle'].value} logs in!")
      end
    end
  end
end

As for next steps, I would suggest checking out the official React getting started tutorial, which isn’t the best documentation I’ve ever seen, but will certainly get you familiar with some concepts. The examples are all in JSX, so it is an exercise left to the reader to translate them to Ruby, but it should be fairly straightforward and a gentle primer to React. After that, I would recommend react for beginners which is an excellent series of videos explaining the concepts behind the framework and a walkthrough detailing how to build a relatively complex app. There are some excellent egghead.io React videos too.

I hope this has been a rewarding exercise and you are now better placed to put React into a Cuba project. Happy coding!

David BushDavid Bush
View Author

David Bush is a Web Developer who travels the world and writes code. His favourite languages are Clojure, Ruby and Python. He enjoys learning new technologies, beer, good food and trying new things. www.david-bush.co.uk

GlennGReact
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week