source Donāt you hate refreshing your browser every time you change a bit of code? This is a lesson from , which helps Ruby on Rails developers become amazing at building UIs in Rails using React.js. Itās only meant for people who are serious about using React in production, so unless youāre one ofĀ them. The Complete React on Rails Course donāt click this link In this lesson, weāre going to look at how to use hot asset reloading in a react_on_rails app with Hot Module Replacement. The app weāre building is a calendar appointments app. You can find the code on : Github _Contribute to calreact-hmr development by creating an account on GitHub._github.com GitHub - learnetto/calreact-hmr Hot reloading means automatically showing live code changes in the browser without having to reload the page every time you change something. Itās quite useful in development. You can have your code and browser windows next to each other and simply write code and save it, and see the changes immediately without switching over to the browser or refreshing it. The uses a special type of hot reloading called Hot Module Replacement or HMR via Webpack Dev server to provide the hot assets to Rails, rather than the asset pipeline. react_on_rails gem When you start the app, Webpack builds the bundle and then continues to watch the source files for changes. If it detects a source file change, it rebuilds only the changed module(s) and updates them in the browser through an HMR runtime. The react_on_rails gem has a on how to set this up. README BUT the example code they provide has a lot of extra bits and itās easy to get lost in npm hell trying to do all of that at once. So I made this super simplified step-by-step tutorial on how to do it.š I recommend you clone the react_on_rails repo anyway on your computer and look in the for the hot reloading setup code example. spec/dummy directory You have to change a number of files and settings, so Iām going to take you through the setup step by step. You can do hot reloading for any assets including , CSS and images but in this lesson weāre only going to use it for Javascript. Javascript Hot reloading is only meant for development, so we need to produce two sets of config filesāāāone for development to use hot reloading and one for production (to use static assets). Letās first do the setup for hot reloading and weāll add the extra setup for serving static files in production, later. 1. Weāre first going to install some javascript packages weāll need. The two key packages are (which enables reloading through a hot module replacement API) and (the development server that serves the live assets). react-transform-hmr webpack-dev-server In addition, we need a babel plugin for applying the transform as well. We need to add these to our : client/package.json file ābabel-plugin-react-transformā: ā^2.0.2ā,āreact-transform-hmrā: ā^1.0.4ā,āwebpack-dev-serverā: ā^1.16.2ā Weāre also going to add jquery and jquery-ujs here so that they are bundled by webpack (instead of Rails) and made available to any other packages that depends on them. ājqueryā: ā^3.1.1ā,ājquery-ujsā: ā^1.2.2ā, Then letās run: $ npm install And thatās the packages done. 2. Next, letās set up the webpack config files. We need a which will be shared by the and the . base config hot config static one Iāve copied these config files from the react_on_rails repo spec/dummy/client directory into my app and simplified them to just use the basics that we need. Letās take a quick look at them. The base config mainly sets up the entry points and file extensions to be resolved. It leaves the output setting for the environment-specific config files. // Common client-side webpack configuration used by// webpack.client.rails.hot.config and webpack.client.rails.build.config. const webpack = require('webpack');const path = require('path'); const devBuild = process.env.NODE_ENV !== 'production';const nodeEnv = devBuild ? 'development' : 'production'; module.exports = { // the project dircontext: __dirname,entry: ['babel-polyfill','es5-shim/es5-shim','es5-shim/es5-sham','jquery-ujs','jquery','./app/bundles/Appointments/startup/registration',],resolve: {extensions: ['', '.js', '.jsx'],alias: {libs: path.join(process.cwd(), 'app', 'libs'),react: path.resolve('./node_modules/react'),'react-dom': path.resolve('./node_modules/react-dom'),},}, plugins: [new webpack.DefinePlugin({'process.env': {NODE_ENV: JSON.stringify(nodeEnv),},TRACE_TURBOLINKS: devBuild,}), ],module: {loaders: [{ test: require.resolve('jquery'), loader: 'expose?jQuery' },{ test: require.resolve('jquery'), loader: 'expose?$' },],},}; It also exposes jQuery as a global so that itās available to any other packages dependent on it. Now letās look at the hot config. It builds on top of the base config. It pulls in the basic config and adds some extra things for hot reloading. It sets the port where webpack dev server will run (3500). // Run with Rails server like this:// rails s// cd client && babel-node server-rails-hot.js// Note that Foreman (Procfile.dev) has also been configured to take care of this. const path = require('path');const webpack = require('webpack'); const config = require('./webpack.client.base.config'); const hotRailsPort = process.env.HOT_RAILS_PORT || 3500; It adds a couple of entry pointsāāāthe first is the live asset from webpack dev server and the second is a module it needs. config.entry.push(`webpack-dev-server/client? ,'webpack/hot/only-dev-server'); http://localhost:${hotRailsPort}` Want this lesson on video? Click on the imageĀ above Then it sets the output filename and path. Weāre just calling it webpack-bundle. config.output = {filename: 'webpack-bundle.js',path: path.join(__dirname, 'public'),publicPath: ` ,}; http://localhost:${hotRailsPort}/` If you look in the , they āāone for the app and one for vendor files. react_on_rails repo example split it into two files But weāre keeping it simple here and just creating one output file. The most important bit here is this loader which takes our jsx files and applies the hmr transform through a babel plugin. config.module.loaders.push({test: /\.jsx?$/,loader: 'babel',exclude: /node_modules/,query: {plugins: [['react-transform',{transforms: [{transform: 'react-transform-hmr',imports: ['react'],locals: ['module'],},],},],],},},{test: require.resolve('jquery-ujs'),loader: 'imports?jQuery=jquery',}); And then we add the hmr plugin to the config and export it. config.plugins.push(new webpack.HotModuleReplacementPlugin(),new webpack.NoErrorsPlugin()); config.devtool = 'eval-source-map'; console.log('Webpack HOT dev build for Rails'); module.exports = config; _Max Rose-Collins, Co-founder and Ruby Engineer at RankTracker You know Rails well. You've been using it for a while, atā¦_learnetto.com The Complete React on Rails 5 Course 3. The next step is to include the correct webpack assets file for each environment in our Rails application layout file , based on whether weāre using hot reloading or not. Weāll use a view helper to configure the correct assets to loadāāāeither the āhotā assets or the āstaticā assets depending on the environment. <!DOCTYPE html><html><head><title>Calreact</title><%= csrf_meta_tags %> <%= stylesheet\_link\_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= env\_javascript\_include\_tag(hot: \['[http://localhost:3500/webpack-bundle.js'](http://localhost:3500/webpack-bundle.js%27)\]) %> <%= env\_javascript\_include\_tag(static: 'application\_static', hot: 'application\_non\_webpack', 'data-turbolinks-track' => true) %> </head> <body><%= yield %></body></html> Here, env_javascript_include_tag is a helper method provided by react_on_rails which includes the hot or static asset file based on whether an environment variable is set to use hot reloading or not. We have two tags here, one to load the hot assets which donāt use turbolinks and another for static assets. And then we need to create these two files. : application_static.js //= require webpack-bundle//= require application_non_webpack We require the webpack-bundle first and then require application_non_webpack. Letās create that file nowāā : application_non_webpack.js.erb <% if ENV["DISABLE_TURBOLINKS"].blank? %><% require_asset "turbolinks" %><% end %> react_on_rails uses this bit of code to require turbolinks based on an env variable. We can use it to easily disable or enable turbolinks. Ok so thatās all set up. Now if we want to use static assets then weāll include just the application_static file which includes webpack_bundle and any non webpack assets. And if weāre using hot reloading then weāll include the hot assets from webpack dev server and the non webpack assets like turbolinks from application_non_webpack. 4. Next we need to make sure that we include the webpack generated files in our assets initializer config file . type = ENV["REACT_ON_RAILS_ENV"] == "HOT" ? "non_webpack" : "static"Rails.application.config.assets.precompile +=["application_#{type}.js"] So again here, based on this env variable we set the name of the file to include in our precompiled assets. 5. Next, we need to create a new Procfile for hot reloading. Weāll call it . Remember, it goes in the root of the app directory. Procfile.hot It has a couple of processes # Procfile for development with hot reloading of JavaScript and CSSrails: REACT_ON_RAILS_ENV=HOT rails s -b 0.0.0.0 # Run the hot reload server for client developmenthot-assets: HOT_RAILS_PORT=3500 npm run hot-assets First the rails serverāāāit runs it with the variable set to . REACT_ON_RAILS_ENV HOT Second, which is a script for serving the hot assets on port 3500 via webpack dev server. hot-assets So thatās Procfile.hot all done. 6. We need to add the server-rails-hot.js script for running the webpack dev server to our client directory. This file imports webpack, webpackdevserver and our hot config and then creates a new dev server and runs it on port 3500. import webpack from 'webpack';import WebpackDevServer from 'webpack-dev-server'; import webpackConfig from './webpack.client.rails.hot.config'; const hotRailsPort = process.env.HOT_RAILS_PORT || 3500; const compiler = webpack(webpackConfig); const devServer = new WebpackDevServer(compiler, {contentBase: ` ,publicPath: webpackConfig.output.publicPath,hot: true,inline: true,historyApiFallback: true,quiet: false,noInfo: false,lazy: false,stats: {colors: true,hash: false,version: false,chunks: false,children: false,},}); http://lvh.me:${hotRailsPort}` devServer.listen(hotRailsPort, 'localhost', err => {if (err) console.error(err);console.log(`=> š„ Webpack development server is running on port ${hotRailsPort}`);}); This file needs to be used by an npm script.And thatās the last missing piece of the puzzle. 7. Letās add some scripts in the two package.json files for npm to run our hot reloading code First, letās do the . package in the root directory "scripts": {"postinstall": "cd ./client && npm install","build:clean": "rm -r app/assets/webpack/* || true","build:dev:client": "(cd client && npm run build:dev:client --silent)","hot-assets": "(cd client && npm run hot-assets)"} The build:dev:client script is for static assets which weāll look at in a minute. But the hot-assets one is what we need for hot reloading. It cdās into the client directory and npm runs another script called hot-assets. So letās add that into the now: client package file "scripts": {"build:test": "npm run build:client && npm run build:server","build:client": "webpack --config webpack.client.rails.build.config.js","build:dev:client": "webpack -w --config webpack.client.rails.build.config.js","hot-assets": "babel-node server-rails-hot.js"}, So here you can see the hot-assets script runs babel-node (a command line tool provided by babel) with our server-rails-hot.js file that we set up earlier. And thatās everything we need to set up to get hot reloading of our javascript working. So letās see it in action now! Letās go to the terminal and fire up foreman with our new Procfile.hot: $ foreman start -f Procfile.hot foreman start -f Procfile.hot You can see Webpack development server is running on port 3500 and our bundle gets compiled successfully. Now letās test our app in the browser. To test hot reloading, Iām going to place my code editor next to the browser and youāll see the live changes I make automatically appear in the browser. Changes made to React components and utilities code appear immediately in the browserĀ š„ If we look at the logs, weāll see that webpack dev server is serving these hot updates. Look at the size of the update files. They are a few bytes. So this is quite fast. Alright, so thatās hot reloading set up for development. 8. Now one final thing is to set up the config for serving static assets to use in production or if we donāt want to use live reloading in development. We need a couple of thingsāāāa static webpack config and a static Procfile. Letās add the first: webpack config // Run like this:// cd client && npm run build:client// Note that Foreman (Procfile.dev) has also been configured to take care of this. const webpack = require('webpack'); const config = require('./webpack.client.base.config'); const devBuild = process.env.NODE_ENV !== 'production'; config.output = {filename: 'webpack-bundle.js',path: '../app/assets/webpack',publicPath: '/assets/',}; // See webpack.client.base.config for adding modules common to both the webpack dev server and rails config.module.loaders.push({test: /\.jsx?$/,loader: 'babel-loader',exclude: /node_modules/,},{test: require.resolve('react'),loader: 'imports?shim=es5-shim/es5-shim&sham=es5-shim/es5-sham',},{test: require.resolve('jquery-ujs'),loader: 'imports?jQuery=jquery',}); config.plugins.push(new webpack.optimize.DedupePlugin()); if (devBuild) {console.log('Webpack dev build for Rails');config.devtool = 'eval-source-map';} else {console.log('Webpack production build for Rails');} module.exports = config; Iāve used the webpack.client.rails.build.config.js file from the react_on_rails spec/dummy example and simplified it to only include the bits we need. Similar to the hot webpack config, it builds upon the base config. It sets the output filename and adds the loaders we need. Now letās add a : Procfile # Run Rails without hot reloading (static assets).rails: REACT_ON_RAILS_ENV= rails s -b 0.0.0.0 # Build client assets, watching for changes.rails-client-assets: sh -c 'npm run build:dev:client' # Build server assets, watching for changes. Remove if not server rendering.#rails-server-assets: sh -c 'npm run build:dev:server' We need the top two processes, one for rails and the other for serving the assets. The last process is for server rendering, which Iāve commented out. The rails-client-assets script runs which we defined in package.json earlier like this: build:dev:client "build:dev:client": "webpack -w --config webpack.client.rails.build.config.js", It runs webpack with the build config file we just created. Ok, so thatās the setup for production. We can test it by running foreman with Procfile.static: $ foreman start -f Procfile.static Weāll no longer get the webpack dev server message in the logs and weāll just have the rails app running on port 5000. Now if we make a change in a component, it wonāt appear automatically in the browser. Weāll need to reload the page manually to see the change. So thatās hot reloading of javascript assets using Hot Module Replacement! š š If you found this tutorial useful, please clickity click the green heart.šš Oh and donāt forget to which has a full detailed video of this lesson and over 3 hours of more lessons which will take you from zero to hero using React with Rails. check out my course This is a lesson from , which helps Ruby on Rails developers become amazing at building UIs in Rails using React.js. Itās only meant for people who are serious about using React in production, so unless youāre one ofĀ them. The Complete React on Rails Course donāt click this link _Max Rose-Collins, Co-founder and Ruby Engineer at RankTracker You know Rails well. You've been using it for a while, atā¦_learnetto.com The Complete React on Rails 5 Course