WARNING: this post is LONG.
This post will be a re-cap of my first hand experience with adopting ReactJS in an existing Rails application, which is in production and used by real users.
We have all seen enough ‘template’, ‘boilerplate’ or ‘starter’ apps showing us how to move our Rails front end development to React. They all work great, if you start from scatch or if your Rails app is still small (meaning your can afford to rewrite a lot of existing front end code).
Unfortunately, it’s not my case. My Rails application’s first commit was done on 9th of March 2012. The app has been developed as a typical Rails app until Aug 2015 (when I introduced React to the dev stack). That’s about 3.5 years of your typical “Rails way” development. This means a lot of HAML, SCSS, Coffeescript files. I simply cannot afford to rewrite everything from scatch.
I obviously started with
react-router-rails. The issue with
my-asset-rails gems is always about the upgradability. At the time writing this post, HEAD of
react-router-rails is still pinned to react-router 0.13.3.
react-router on npm is already at 2.0.1. Since I am not using server side render, using
my-asset-rails gems to just use their UJS hooks doesn’t seem to be a smart idea.
As someone who’s big on being pragmatic, I removed
react-router-rails and simply registered the following JS object in the head of the rails layout file
In my application, I decided to render at most one main react component for any given Rails route. There’s no magic in the above code. It simply plays within the rules I set on my own app. It lays out an object with a few values to be filled by the Rails view file (discussed later) and a couple of mount functions (One for plain React components; the other for react-router wrapped components). The reason why the above JS code needs to be included in the layout header is that my app’s main
application.js is loaded async at the end of the HTML
body using the following JS code.
application.js manifest file is the beast. It
requires for all those good old coffeescripts, as well as 2 special pieces.
- One being the webpack transpiled js bundle file,
dist_react_components.js, for all of my React components
- The other being a special
mount_react_component.js file is super simple. When it comes to life (loaded on to DOM asynchronously), it invokes one of the
LazyReactComponent’s mount functions depends on the
LazyReactComponent.type value set by the Rails view file (I’ll go into later).
dist_react_components.js file exposes all mountable React backed UI components in a JS object literal. It’s something like below. Note that there’s no
export, since this file would be used outside the module system. It’s included by Rails’
After I write the ActivityReport React component, I now only have 1 thing left to do. If I have a Rails route like below
We all know how to do the normal Rails controller, action, view stuff. The only thing interesting here is the view file,
JS snippet is actually being written in the erb view file. It simply sets up the required values on the
LazyReactComponent’s mount methods ready to be invoked by
mount_react_component.js discussed above.
What’s described above has been a journey for me. Many trials and errors. My goal is to gradually introduce React to an exisitng Rails team working on a Rails app without compromising team’s productivity. After about 6 months of pushing forward, I can proudly say I achieved my initial goal. We still build assets using the Rails asset pipeline. During development, only extra step for developers is to remember running
npm run watch-js in their local terminal consoles. The npm
watch-js script is plain simple,
In future posts, I’ll discuss more about my learnings around