If you're developing a web application using React and ES6/ES2015, you may have cursed at the computer or impatiently tapped your fingers while waiting for files to compile and then having to manually reload the browser to see the latest changes. I used to use Gulp to help streamline the process of watching source files and rebuilding them with Webpack on any changes. That helped, but it can still take a while, especially if you have a large app.
Then, I started to embrace hot module replacement (HMR), or hot reloading, using Webpack. In short, this just means whenever your source files are modified and saved, only the changed module is rebuilt instead of every single module in your project. Then, the HMR runtime (your browser) will automatically listen for any patches, apply the patches (if possible), and then automatically reload. This all happens pretty fast and it speeds up development time!
Target audience
There are several methods to implementing HMR. This tutorial is specifically aimed towards the following:
- You want to use your own Express 4 server instead of the webpack-dev-server that you may have came across
- Your server-side code is written in ES6, and you are using Babel's require hook:
require('babel-register')
- You want to use react-transform-hmr
Let's get down to it.
Prerequisites
I assume you already have Express and React installed and all set up. I'll walk you through what you'll need for Babel and Webpack. Install the following npm packages:
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-react-hmre webpack webpack-dev-middleware webpack-hot-middleware
Here we are using babel-preset-react-hmre
. This is a convenience package that sets up react-transform-hmr
as a preset for Babel.
Setup
Babel
Create a .babelrc
file in your project's root directory, and put the following in the file:
{
"presets": ["react", "es2015"]
}
This allows Babel to transpile your React JSX and ES6 code.
Webpack
Create a webpack.dev.config.js
file in your project's root directory, with the following:
var webpack = require('webpack')
module.exports = {
resolve: {
extensions: ['', '.js', '.jsx']
},
entry: ['webpack-hot-middleware/client', './app.js'],
output: {
path: __dirname + '/build/js',
filename: 'bundle.js',
publicPath: '/public/js'
},
devTool: 'cheap-module-source-map',
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['react-hmre']
}
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
};
You can modify whatever settings you want based on your project or preferences, but just make sure you do the following:
- Include
webpack-hot-middleware/client
as an entry point - Set
output.publicPath
to the path the client expects to find the bundled js file, e.g., if your html page has<script src="/js/bundle.js"></script>
, then yourpublicPath
would be/js
. This is where the virtual bundle for HMR will be served from. - For the JS/JSX loader, use
babel
(you can also usebabel-loader
) and include thequery
option for referencing thereact-hmre
preset - Include
HotModuleReplacementPlugin
Also notice how we named the file for the dev environment. You wouldn't be using HMR in production, so you should make a separate Webpack config file just for production that doesn't use HMR.
Another thing to point out is that the react-hmre
preset is set up in the config file. Normally, you would set up Babel settings in .babelrc
. However, again, this tutorial is geared towards using ES6 on the server-side, and if you use Babel to transpile the server-side code, you're probably using their require hook.
Now, you don't want to want to enable hot reload on the server; you just need it on the client side. So if you do require('babel-register')
, that will load .babelrc
, and you don't want the HMR preset in there to be loaded on the server side.
Express server
In your server, add the following before your other middleware:
if (process.env.NODE_ENV === 'development') {
let webpack = require('webpack'),
webpackConfig = require('./webpack.dev.config.js'),
compiler = webpack(webpackConfig);
server.use(require('webpack-dev-middleware')(compiler, {
noInfo: true,
publicPath: webpackConfig.output.publicPath
}));
server.use(require('webpack-hot-middleware')(compiler));
}
And that's it! Simply launch your server, bring it up in your browser, make a change to a source file, and see the browser automatically reload to include the changes!