Setting up react in ruby on rails - Part 1


React

React is the cool kid on the Javascript block at the moment. So in this post I'm going to look at how to set up a rails stack with react and the following FE tools:

Before I go into the how, it might be worth explain the why. So let's talk about why you need these in your FE stack.

React

So react is the View part of MV* set up in JS. It has a good focus on performance. With it's 'Virtual dom', it makes manipulating the 'DOM' pretty quick. It's also allows for the holy grail of MV* world server pre-rendering. Through the excellent rails-react gem it opens this up to Rails developers

Browerify & NPM

I'm sure you've had this problem before, your JS manifest in spockets in the wrong order and now your javascript is broken. Worse still you have conflict because two variables have been named the same. A module loaders help resolve this problem. It allows you to declare the js you want, in the modules you want, without breaking anything:

//Module 1 - module1.js
var dosomething =  require('my-module');
dosomething();
//Module 2 - module2.js
var dosomething =  require('other-module');
dosomething();

There are many module loaders - like requirejs or webpack. But browserify integrates well with asset pipeline through rails-browserify gem. Because it works through the asset pipeline it works much easier with react-rails gem.

This uses the commonjs approach to modules (that node use), it allows us to use NPM. It's sort of a version of ruby gems, with bits of bundler thrown in. (although not quite as good bundler tbh)

Karma & Jasmine

Most ruby devs know testing is important. Karma and jasmine is a great approach for testing your Javascript.

Jasmine is a JS equivalent to rspec or minitest and lets you write assertions to test your code. Other test frameworks, like Mocha with chai, sinon or qunit are also great options.

Whatever you choice it will work well with karma which test runner. This is a tool to run your tests. You can run your tests across many browsers through the terminal. Great for quick cross browser testing . You can even run your test through multiple browser at once.

A note on Jest

Jest is the recommended testing framework for react. But I'll be honest I tried it and didn't get on with it:

  • It feedback, error reporting and debugging tools where non-existent. This makes debugging issues painful.
  • It won't let you run the tests in your browser (like karma)
  • If you want to continuously running your tests, you'll need to use something with Jest (like gulp).

I've also read a lot that it is quite slow, and these are my reasons for not using it.

Eslint

Linting is a great way to keep code consistency. It also pick up on any issues or bad practices in the code. ESlint is a great tool for linting JS, it's easily configurable and works well with both react and ES6 code.

Babel

Babel is a great build system to let you write ES6 features today. It has excellent coverage for current spec. It is also being used now in react

## Setting up

I like to thank James Burnett for his excellent tutorial on setting up react and rails. I based this on with a few adaptations of my own, like setting up pre-rendering.

So the first thing is you'll need to do is add to your gemfile:

gem 'browserify-rails'
gem 'react-rails', '~> 1.0'

then bundle install

Setting up NPM/Browserify

Next you'll need to install node, if you haven't installed it already install. I'd recommend doing it through homebrew if your on a mac.

Next run in your terminal:

npm init

Now run through the set up to create your package.json in the root of your project.

Next will need to install the following browserify modules:

npm install --save-dev browserify browserify-incremental babelify

You should find node_modules folder has been created in the root of the project. Also your package.json has been updated.

  • Browserify-incremental is a requirement of the browserify-rails gem.
  • Babelify allows us to use babel with browserify

Now as we're managing are JS through NPM run

npm install --save react

Once this is done open up your config application.rb and add the following lines:

config.browserify_rails.commandline_options = "-t babelify --extension=\"es6.js\""
config.browserify_rails.source_map_environments << "development"

The first line will run babelify with browserify and run it over all the files labelled 'es6.js' (You can adapt this as you which). If you want add other options check the babelify repo.

The second line enables sourcemaps in development, which are useful for debugging.

Browserify is now set up. We can test this by adding to the application.js:

var React = require('react/addons');

If you start up your server and go to http://localhost:3000/asset/application.js

You should be able to see that the react code is now compiled into the application.js.

React and react-rails

So now we need to set up react, the first thing we need to do is run the react setup:

rails g react:install
  • Just allow it to replace your application.js file we need to tweak it anyway.

This will update your application.js, as well as add a components.js and components folder. This is to add your react components too.

It will set up the standard sprocket declarations for react. But as we are using browserify and also for testing we will need to change this. So we'll tweak the manifest to the following:

// <<<<< Old manifest
//= require react
//= require react_ujs
//= require components
// <<<< Change too:
//= require_self
//= require components
//= require react_ujs

We then add the following line to the application.js:

var React = window.React = require('react/addons');

What this does is set your node version of react to the global scope. This allows us to use this version of react, instead of the gem version. This will make it work with the react_ujs, without effecting or browserify modules or test set up.

Next lets set up a simple react component. So paste the following code into a new js file in components called test-component.es6.js:

React   = require('react/addons');
let TestComponent = React.createClass({
render() {
return (<h1>Hello world</h1>);
}
});
module.exports = TestComponent;

Now you may notice 2 things here, firstly that I haven't applied a var to the react variable. Bad practice right? Well it's a bit of a hack, but as we've already declared react to the global scope, we don't want to declare it again. This is because react tries to instantiate twice and throw and massive error. So declaring like this is a bit weird it the best way to get them working together.

The other thing is as we have added the .es6.js extension. We can start using es6 features like 'let' as an example.

The react gem needs everything on the global scope. So we'll need to add are new module to the global scope.

We could declare each module individually, but this can get difficult to manage. I like to do it in the following way, so in your component.js file:

// You'll probably want this if your using babel
require("babelify/polyfill");
var app = window.app = {};
//React bootstrap modules
var TestComponent = require('./components/test-component.es6.js');
app.TestComponent = TestComponent;

So what I have done is created an app object onto our global scope. We have then apply our components to this.

Now if you haven't create a homepage create one and add the following to it:

<%= react_component 'app.TestComponent' %>

If you boot up your app and look at your home you should now see your 'Hello world' component.

now if we tweak are component the 'this.props.title', like so:

let TestComponent = React.createClass({
render() {
return (<h1>this.props.title</h1>);
}
});

We can now tweak our view like so:

<%= react_component 'app.TestComponent', {title:"Hello, Back"} %>

You should now see "Hello, Back" in you component. This is a nice way to pass data to your component from the view.

Pre-rendering

So one of the great features added to 1.0 release react-rails gem is the ability to pre-render your components. This is great for both SEO and your users perception of how long the page takes to render.

Now if we add the pre-render tag to our view like so:

<%= react_component 'app.TestComponent', {title:"Hello, Back"}, {prerender:true} %>

You'll see a massive error in your view, this is because it uses execjs to run the JS on the server***. The react-rails gem is using global variable to render them into your view. (). So you'll need to put 2 tweaks into your JS, firstly in your application.js

*** Thanks to Olivier Lance on his article that cover this

var React = window.React = global.React = require('react/addons');

And secondly in our components.js file:

var app = window.app = global.app = {};

This will open up our React and app variable to global variable it uses to render.

Now restart you app and you should find that hello world example is prerendering! (You can check by looking at the source) **

** N.B. If it's still throwing an error, it worth running 'rake tmp:cache:clear'. This will flush the browserify-rails cache.

Next steps

So now that we have react and browserify set up. he next steps are to set up our testing and linting, which I will cover in part 2 of the post.


1 Comment

Nedich
Nedich
August 14, 2015

Thank you very much for this tutorial ! When I have another component defined in lets say TestComponent, where do i need to require it ?


Got Something to Say?

Don’t use these services? Create an account or