12/08/2018, 15:31

Hot Reloading React Using Webpack

What Is Hot Reload? Hot reloading is a technique to keep the app running and to inject new versions of the files that you edited at runtime. This way, you don't lose any of your state which is especially useful if you are tweaking the UI. This can save you a lot of time and effort during ...

What Is Hot Reload?

Hot reloading is a technique to keep the app running and to inject new versions of the files that you edited at runtime. This way, you don't lose any of your state which is especially useful if you are tweaking the UI. This can save you a lot of time and effort during development process.

Installing Required Packages

Before we can move on to configuration step, we need to install these packages:

  • react
  • react-dom
  • webpack
  • webpack-dev-server
  • react-hot-loader

Configure Webpack

Lets take a look at the following webpack config for normal development flow before adding hot reload.

const path = require('path')
const webpack = require('webpack')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

module.exports = {
  entry: {
    bundle: ['./src/app.js'],
    vendors: ['react', 'react-dom', 'redux']
  },

  output: {
    publicPath: '/',
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },

  module: {
    rules: [
      {
        test: /.(js|jsx)$/,
        exclude: /node_modules/,
        use: ['babel-loader']
      },

      {
        test: /.(jpg|jpeg|png|gif|ttf|woff|woff2|otf|eot|svg)(?v=[0-9].[0-9].[0-9])?$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 10000,
              name: '[name].[ext]'
            }
          }
        ]
      },

      {
        test: /.(scss|css)$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
      }
    ]
  },

  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),

    new webpack.optimize.CommonsChunkPlugin({
      minChunks: 2,
      names: ['bundle', 'vendors'],
    }),
    
    new ExtractTextPlugin({
      filename: '[name].css'
    }),

    new webpack.DefinePlugin({
      DEBUG: JSON.stringify(true)
    })
  ],

  devtool: 'cheap-module-eval-source-map'
}

In order for this configuration to support hot module reloading we need to change a few things. First of is to add react-hot-loader/patch to the bundle entry point. This will add what neccessary for webpack-dev-server to start working. Note: the order react-hot-loader must come first.

// Before
bundle: ['./src/app.js']

// After
bundle: [ 'react-hot-loader/patch', './src/app.js']

Next is to change the configuration for loading style. Because ExtractTextPlugin will put style in separate file, there is no way for webpack-dev-server to know what will be change. To support hot reloading style as well we need to remove ExtractTextPlugin from our configuration.

// Remove these lines
new ExtractTextPlugin({
  filename: '[name]-[chunkhash].css'
})

We need to modify our bootstrap code to incorporate hot reloading. A tipical react bootstrap will look like this.

// more import
...
import App from 'containers/app'

document.addEventListener('DOMContentLoaded', () => {
   ReactDOM.render(
     <AppContainer>
        <Provider store={store}>
          <App />
        </Provider>
      </AppContainer>
   , document.getElementById('root'))
})

After adding hot reloading it will become like this

// more import
...
import App from 'containers/app'

document.addEventListener('DOMContentLoaded', () => {
  const render = (Component) => {
     ReactDOM.render(
       <AppContainer>
        <Provider store={store}>
          <Component />
        </Provider>
      </AppContainer>
   , document.getElementById('root'))
  }
  
  render(App)
  if (module.hot) {
    module.hot.accept('containers/app', () => {
      render(require('containers/app').default)
    })
  }
})

And last but not least, change npm script. The --hot, as the name suggest, is for hot reloading and as for --history-api-fallback for when using when with react-router that configure to work with HTML5 history API. It is recommended that you turn this on.

// Before
"build": "webpack-dev-server --inline"

// After
"build": "webpack-dev-server --hot --inline --history-api-fallback"

And there you have it, just rebuild the app edit some js files and see the magic happen.

0