Short story: two JS developers have leave the company for valid and urgent reasons, ergo, I’m developing some sections of the front-end using ReactJS libraries.

Quick review:

React is developed by Facebook.

React uses a virtual DOM to render and update the view. The feeling for the final user is a fast and very responsive page.

It uses JSX, that means that you will include XML-kind of syntax in your code, which is not as bad as it sounds. Your files will have the extension .jsx

It uses ES6, the new version of JavaScript but you need to compile it before be able to run it in the “browser run-time environment”.

Well, lets go!

$rails new react_rails && cd react_rails

First tip: don’t mix rails and react, don’t use gems like react-rails. If you mix them you will pollute both environments and the debugging and the testing for Ruby and JS code will be the hell on Earth. My recommendation is that the file:

RAILS_HOME/app/assets/javascripts/application.js

in your project, should be empty and from now Rails will be blind about JavaScript. But that, after all, is your call.

Create a new dir:

$mkdir -p lib/assets/frontend/javascript && cd lib/assets/frontend/javascript

From now this will be our REACT_HOME dir and won’t be related with our Rails code at all.

Tip two: Don’t put your React project inside RUBY_APP/assets/ because rails will try to compile the 300 MB that is inside the node_modules directory.

If you don’t have any, create a package.json file:

$npm init

We can install react now:

$npm i react react-dom --save

A React component is pretty simple:

// file: App.jsx

import React from 'react'; class App extends React.Component {

constructor(props) {

super(props);

this.state = {

salute: 'Hello (Virtual DOM) World !!!',

countries: [{name:'Argentina:', capital:'Buenos Aires'},

{name:'Peru', capital:'Lima'},

{name:'Uruguay', capital:'Montevideo'}]

}

this.handleClick = this.handleClick.bind(this); // linking the method scope

}



handleClick(){

this.setState({salute: 'Hey!!, a new salute!'});

}

render() {

return (

<div>

<h2>{this.state.salute}</h2>

{ this.state.countries.map((object, i) => {

return <div key={i}> Country: <b>{object.name}</b> and capital: <b>{object.capital}</b> </div>

})}

<p id="onclick"><button onClick={this.handleClick}>Change title</button></p>

</div>

)

}

}

export default App;

React has two sets of data: this.props (properties) and this.state. Props are used to pass information between different components like when a Parent component pass data to a Child component. Meanwhile this.state is used inside the component.

As you will see, one of the best features of React components is that the method this.setState() automatically updates the virtual DOM (the view in our page).

Note that the export part in the component is important , if is not set, React won’t be able to find the component. The default part is also relevant: to import a component that was exported as the “default” one you won’t need curly brackets:

import App from './App.jsx'; // <-- App was exported as default import {App} from './App.jsx';// <-- App wasn't exported as default

Now you can import your component and render it in the Rails view:

//file: index.jsx

import React from 'react';

import ReactDOM from 'react-dom';

import App from './App.jsx';



ReactDOM.render(<App />, document.getElementById('react_entry'));

The bad news is that we need to compile the code, so we need Webpack. What Webpack does is create a beatiful bundles.js file with all that we need, so:

$npm install webpack webpack-dev-server babel-loader babel-core babel-preset-react babel-preset-es2015 babel-preset-react-hmre babel-polyfill --save

Our Webpack config file for development looks like:

// file: webpack.config.devel.js var path = require(‘path’);

var webpack = require(‘webpack’);

entry: “./index.jsx”,

debug: true,

devtool: ‘cheap-module-eval-source-map’, // For production, use cheap-module-source-map.

output: {

path: path.resolve(__dirname, “build”),

publicPath: “

filename: “bundle.js”

},

resolve: {

extensions: [‘’, ‘.js’, ‘.jsx’]

},

module: {

loaders: [

{test: /\.jsx?$/, loader: ‘babel’, exclude: /node_modules/, query: {

cacheDirectory: true,

presets: [‘es2015’, ‘react-hmre’, ‘react’]

}, include: path.app

},

{ test: /\.css$/, loader: ‘style-loader!css-loader’},

{ test: /\.js$/, loader: ‘babel’, exclude: /node_modules/, query: {

presets: [ ‘es2015’, ‘react’, ‘react-hmre’ ]

}},

{ test: /\.less$/, loader: ‘style!css!less’ },

{ test: /\.scss$/, loader: ‘style!css!sass’ },

{ test: /\.json$/, loader: “json-loader” },

{ test: /\.woff(2)?(\?v=[0–9].[0–9].[0–9])?$/, loader: “url-loader?mimetype=application/font-woff” },

{ test: /\.(ttf|eot|svg)(\?v=[0–9].[0–9].[0–9])?$/, loader: “file-loader?name=[name].[ext]” },

{ test: /\.gif$/, loader: “url-loader?mimetype=image/png” }

]

},

devServer: {

hot: true,

inline: true,

historyApiFallback: true

},

node: {

fs: “empty”

},

plugins: [

new webpack.ProvidePlugin({

$: “jquery”,

jQuery: “jquery”

})

]

}; module.exports = {entry: “./index.jsx”,debug: true,devtool: ‘cheap-module-eval-source-map’, // For production, use cheap-module-source-map.output: {path: path.resolve(__dirname, “build”),publicPath: “ http://localhost:8080/build/ ",filename: “bundle.js”},resolve: {extensions: [‘’, ‘.js’, ‘.jsx’]},module: {loaders: [{test: /\.jsx?$/, loader: ‘babel’, exclude: /node_modules/, query: {cacheDirectory: true,presets: [‘es2015’, ‘react-hmre’, ‘react’]}, include: path.app},{ test: /\.css$/, loader: ‘style-loader!css-loader’},{ test: /\.js$/, loader: ‘babel’, exclude: /node_modules/, query: {presets: [ ‘es2015’, ‘react’, ‘react-hmre’ ]}},{ test: /\.less$/, loader: ‘style!css!less’ },{ test: /\.scss$/, loader: ‘style!css!sass’ },{ test: /\.json$/, loader: “json-loader” },{ test: /\.woff(2)?(\?v=[0–9].[0–9].[0–9])?$/, loader: “url-loader?mimetype=application/font-woff” },{ test: /\.(ttf|eot|svg)(\?v=[0–9].[0–9].[0–9])?$/, loader: “file-loader?name=[name].[ext]” },{ test: /\.gif$/, loader: “url-loader?mimetype=image/png” }},devServer: {hot: true,inline: true,historyApiFallback: true},node: {fs: “empty”},plugins: [new webpack.ProvidePlugin({$: “jquery”,jQuery: “jquery”})};

Normally, when we are using Webpack, what we want is to generate a bundle.js file to put it in our site. One of the question that you should be asking yourself is how the heck a huge bundle file can be debugged? In the old days, when JS projects were a set of small files with jQuery methods, the errors appeared in the browser’s console, but when all the files are compiled and putting in a single file you can’t do that anymore. The solution is enable source-maps in your browser, that will allows you to find the errors in the JS code.

To generate the bundle.js file is fine when we want to set the file in a production environment, but is annoying when we are in the development process. Instead we’ll use Webpack dev server to put the file “On-line”, that is a faster and better option.

Anyway, we can compile our code now:

./node_modules/webpack-dev-server/bin/webpack-dev-server.js --config ./webpack.config.devel.js --hot --inline --progress --colors --watch --display-error-details --display-cached --content-base ./

This command won’t create a bundle.js file in the hard disk, instead it will put the file in the 8080 port. You can see it in your browser: http://localhost:8080/build/bundle.js

Webpack Hot-Reload feature is a live changer feature and you’d love it. It makes the development process much more fun and efficient. Anyhow, now we need a Rails view to load the React code in it:

$bin/rails generate controller Greetings hello

Change the hello.html.erb view with this lines:

<h1>React Entry view</h1> <div id=”react_entry”></div> http://localhost:8080/build/bundle.js' %>

Start your server:

$ RAILS_ENV=development bin/rails s

Put your browser at: http://localhost:3000/greetings/hello

You should see the “hello world” message. Now, one of the coolest things about the Webpack HotReload feature is that you don’t need to reload the browser, just change some words and values in the App.jsx and see by yourself.

In the next tutorial we’ll see that while our WebApp grows, handle its states becomes more and more complicated and for that reason we must use Redux, a library that “wraps” all our environment and let us put order in our states.