« Previous Tutorial Next Tutorial »

In our last tutorial, we learned that having to re-run our webpack script every single time we changed anything in our code would be incredibly tedious and frustrating. So let’s not do that! There are modules that will not only allow Webpack to detect that a file has changed and rebuild the bundle, but to actually refresh your React app in your browser without even refreshing the page. Living in the future is awesome!

Getting this set up will require installing a couple modules, and will also require substantial changes to our Webpack config file (and even then, we won’t be done – there’s still CSS compiling to tackle). The config is about to get pretty complicated. This is the nature of Webpack, nothing we can really do about that, but I’ll talk you through everything as best as possible.

Let’s get started. Switch to a terminal window, make sure you’re in your musiclist directory, and type the following:

yarn add --dev webpack-dev-server@2.4.5 react-hot-loader@3.0.0

Let that do its thing and then jump back to Sublime Text. AirBnB’s ESLint rules don’t like us using dev dependencies in our code, but we’re going to have to do so here in one instance, so let’s keep them from complaining. Open up .eslintrc, add a comma to the end of line 11, and just below it add the following:

"import/no-extraneous-dependencies": ["error", {"devDependencies": true }],

Save that and we’re good. Now it’s time to tackle .babelrc, so open that up. We’re not making many changes here, but we do need one thing. Add a comma at the end of line two, and then below that add the following:

"plugins": ["react-hot-loader/babel"]

That’s it. Save that, and let’s tackle the big one … webpack.config.js. We’re going to be adding a whole lot of stuff to this file. Let’s start with a couple of changes at the top, and then let’s go on a bit of a tangent. First, replace line 1 const path = require('path'); with the following:

const { resolve } = require('path');
const webpack = require('webpack');

We’re doing this because we don’t need the entire path module, just its resolve method. This saves on bundle size, which becomes significant pretty quickly with Webpack and React (we’ll discuss strategies for minimizing that size in later tutorials).

Next, just below line four, module.exports = {, add the following:

context: resolve(__dirname, 'src'),

This is telling Webpack the base directory from which to resolve our entry points. The reason we didn’t have this line before is because by default, Webpack assumes the current working directory, but it’s better to be specific.

And now, the tangent. Let’s talk about resolve, and about that pesky __dirname that keeps popping up. resolve is one of several methods used for reading and manipulating paths. Essentially, it comes in handy for making sure that your application can find files whether or not it’s running on Windows, OSX, FreeBSD, Ubuntu, MS DOS 3.1 (err … OK, probably not), or any other OS of your choosing.

__dirname is a global Node variable that represents the path to the executable that node is currently running. In our case, that means app.js. This is important because there will be times when you are executing your node application from a directory that is NOT the application’s home directory (this is especially true when running production code on servers), which means that your current working directory is not the same thing as the directory in which your app actually lives. We want to generate our file location paths for both our application and for webpack using the latter, so we use __dirname.

To sum up, what we’re saying with this context line is “resolve an absolute path based on the directory in which our running executable lives, regardless of what OS on which this is running, and add /src to the end of it.” – or, in simpler terms, “hey Webpack, compile everything in /src.”

All right, onward with our config edits. We need to rewrite our entry block almost completely; we’re not using an object anymore, but instead an array of values. Turns out the object shouldn’t be necessary for scss compilation later down the road; there are other ways to handle it. Here’s what your new entry block should look like:

  entry: [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:8080',
    'webpack/hot/only-dev-server',
    './index.jsx',
  ],

First we enable the hot reloader for React, then we establish the Webpack dev server, which is a separate server from our application’s webserver and set it up to run at localhost:8080. You can change this port if you want, but we’re going to reference it later, so make sure you change it there, too. After that, we tell Webpack to hot-reload only when webpack compiles successfully. Finally, we tell it where to start … index.jsx (we don’t need /src because of the context block we already discussed).

We could theoretically use Webpack’s dev server to serve our entire app, except that our app is a Node.js-based API, not a simple HTML-and-CSS website. That’s why we have to keep our app running at localhost:3000 as we’ve been doing.

We also need to rewrite our entire output block—sorry, the thing with doing this in tiers to make it clearer and more understandable is that sometimes we have to throw away code—so let’s do that. Here’s what you need:

  output: {
    filename: 'build.js',
    path: resolve(__dirname, 'public', 'javascripts'),
    publicPath: '/javascripts',
  },

We’re replacing the name variable with build.js because we’re no longer providing a name in our entry block. We’re changing the path to include javascripts, and we’re setting the publicPath because webpack needs it to know where to load hot-update chunks (parts of the file it’s replacing).

Below the output block, add a whole new block, which configures our dev server, like this:

  devServer: {
    hot: true,
    contentBase: resolve(__dirname, ''),
    publicPath: '/javascripts',
  },

This sets hot-reloading to true, explains that the content base path is the same as our app’s base path, and that we want our build file to be served at localhost:8080/javascripts

We’re almost done. Our resolve block remains unchanged for now, as does our module block. Hooray! Below those, we need a new block called plugins, which should look like this:

  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NamedModulesPlugin(),
  ],

This just calls a couple of plugins that Webpack needs to do hot module replacement, which is a technical term for “updating you react bundle without having to rebuild the entire thing every time.”

We’re good here. Save your file, and in the next tutorial we’ll configure the rest of our app for hot reloading, get it running, and see it in action as we change some things and watch the browser update without any effort on our part. See you there!

« Previous Tutorial Next Tutorial »