Will Little Subscribe

Adding Webpack with Hot Module Replacement to speed up Redux/React/Sass development within a Rails5-app


Piggy-backing on my previous post, Rails5, ActionCable, Redux, and React: Walking through an example chat application, I was curious how difficult it would be to spin up a webpack server with hot module replacement (HMR) alongside Rails (i.e. firing it up on another port) to speed up development of the Redux/React/Sass parts of the app.

Dan Abramov, the godfather of all-things hot-reloading/React/Redux, recently said:

…if you do use something like Redux, I strongly suggest you to consider using vanilla HMR API instead of React Hot Loader, React Transform, or other similar projects. It's just so much simpler — at least, it is today.

Ok cool. So we won't be preserving state or DOM. We'll live with it for now. It's still going to be a ton faster than manual reloads.

Thus, I began by adding the appropriate set of devDependencies in our existing package.json file:

...
“devDependencies: {
 babel-loader: ^6.2.2,
 bourbon-neat: ^1.7.2,
 css-loader: ^0.23.1,
 file-loader: ^0.8.5,
 image-webpack-loader: ^1.6.3,
 node-bourbon: ^4.2.3,
 node-neat: ^1.7.2,
 node-sass: ^3.4.2,
 react-hot-loader: ^1.3.0,
 sass-loader: ^3.1.2,
 style-loader: ^0.13.0,
 webpack: ^1.12.13,
 webpack-dev-server: ^1.14.1”
 },
...

Then a quick npm install in the root folder gets us prepped.

Next, we setup a webpack.config.js file in the root folder:

Essentially what's going on here is that webpack is expecting an app.js and index.html file in a folder we'll create called webpack_app, and it will output the result of the webpack compilation magic into a webpack_dist folder that it will automatically create.

The three loaders that you see above (lines 20–34) ensure that our javascript, html, and sass files will load correctly, including the correct hookups with bourbon and bourbon neat.

APP.JS

In the webpack_app/app.js file itself then we'll setup the initial objects that will be sent into Redux store and grab React components and Sass files from within our traditional Rails asset pipeline location:

INDEX.HTML

Then, our webpack_app/index.html file only needs to be:

Assuming our “npm install” has been run, a simple “npm start” (aliased to “webpack-dev-server — hot — inline” in our package.json file, BTW) will fire up our webpack server on port 8080.

CLEANING UP THE JS INSIDE THE RAILS APP A BIT

Since we're skipping Rails all together with our webpack dev server, this means we're not loading the asset pipeline, and not loading ActionCable. Thus, we'll need to add some conditionals like this…

if (typeof App !== "undefined"){...}

… in our Root.js container and Chat.js component to avoid the ActionCable workflow. However, given we'd still like to see new chats added to the list, we can hookup the applicable component function like so to update the store:

const handleKeyUp = (e) => {
  if(e.keyCode == 13){
    if (typeof App !== "undefined"){
      App.room.speak(e.target.value);
    }else{
      addMessage({id: messages.length + 1, content: e.target.value})
    }
    e.target.value = “”;
  };
};

LOADING IMAGES AND FONTS

Of course, this is all fine and dandy as an example application, but when it's time to build something real where we need images and fonts, we could either have webpack copy over our images for us when we fire up the server by setting up these kinds of loaders in our webpack.config.js file:

…while requiring the images and fonts appropriately in our app.js file:

…OR we could simply load our images from our complimentary Rails server (localhost:3000).

In fact, if you are following along and prefer to load images/fonts from the Rails server, a good homework assignment would be to modify the initial store object we send into Redux to add a asset_host parameter and set it to http://localhost:3000. Then, for our Rails app we'd make sure that parameter was set to match ActionController::Base.asset_host and build our app accordingly.

And that's it! Now we can quickly build out our React components and Redux workflow on port 8080, and use port 3000 for our more traditional Rails development.

Author's note: for quick reference, you can view all of the changes necessary to add webpack/HMR to a Rails5 app, including some initial bourbon/neat files, in this single commit. Drop me a line if you end up using this stack for a project, as I'm always curious to learn how other developers setup their workflows and specific patterns to scale out redux/react applications. Also, subscribe to my newsletter and/or follow me on twitter or linkedin and I'll let you know when I get new content up. Let's keep the conversation going. Thanks!