Skip to content

Commit 6177520

Browse files
authored
Merge pull request #64 from jmreidy/content-howto-hmr
Content - howto hmr
2 parents 850ec59 + ab0dce4 commit 6177520

File tree

2 files changed

+510
-0
lines changed

2 files changed

+510
-0
lines changed
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
---
2+
title: How to Configure Hot Module Replacement with React?
3+
---
4+
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an
5+
application is running without a page reload.
6+
HMR is particularly useful in applications using a single state tree,
7+
since components are "dumb" and will reflect the latest application state, even
8+
after their source is changed and they are replaced.
9+
10+
##Project Config
11+
This guide will be demonstrating the use of HMR with Babel,
12+
React, and PostCSS (using CSS Modules).
13+
To follow along, please add the following deps to your `package.json`:
14+
15+
To use HMR, you'll need the following dependencies:
16+
17+
```shell
18+
19+
```
20+
21+
In addition, for the purposes of this walkthrough, you'll need:
22+
23+
```shell
24+
25+
```
26+
27+
28+
###Babel Config
29+
Your `.babelrc` file should look like the following:
30+
31+
```js
32+
{
33+
"presets": [
34+
["es2015", {"modules": false}],
35+
//Webpack understands the native import syntax, and uses it for tree shaking
36+
37+
"stage-2",
38+
//Specifies what level of language features to activate.
39+
//State 2 is "draft", 4 is finished, 0 is strawman.
40+
//See https://tc39.github.io/process-document/
41+
42+
"react"
43+
//Transpile React components to JS
44+
],
45+
"plugins": [
46+
"react-hot-loader/babel"
47+
//Enables React code to work with HMR.
48+
]
49+
}
50+
```
51+
52+
###Webpack config
53+
While there's many ways of setting up your Webpack config - via API,
54+
via multiple or single config files, etc - here is the basic information
55+
you should have available.
56+
57+
```js
58+
const { resolve } = require('path');
59+
const webpack = require('webpack');
60+
61+
module.exports = env => {
62+
return {
63+
entry: [
64+
'react-hot-loader/patch',
65+
//activate HMR for React
66+
67+
'webpack-dev-server/client?http://localhost:8080',
68+
//bundle the client for webpack dev server
69+
//and connect to the provided endpoint
70+
71+
'webpack/hot/only-dev-server',
72+
//bundle the client for hot reloading
73+
//only- means to only hot reload for successful updates
74+
75+
76+
'./index.js'
77+
//the entry point of our app
78+
],
79+
output: {
80+
filename: 'bundle.js',
81+
//the output bundle
82+
83+
path: resolve(__dirname, 'dist'),
84+
85+
publicPath: '/'
86+
//necessary for HMR to know where to load the hot update chunks
87+
},
88+
89+
context: resolve(__dirname, 'src'),
90+
91+
devtool: 'inline-source-map',
92+
93+
devServer: {
94+
hot: true,
95+
//activate hot reloading
96+
97+
contentBase: '/dist'
98+
//match the output path
99+
100+
publicPath: '/'
101+
//match the output publicPath
102+
},
103+
104+
module: {
105+
loaders: [
106+
{ test: /\.js$/,
107+
loaders: [
108+
'babel',
109+
],
110+
exclude: /node_modules/
111+
},
112+
{
113+
test: /\.css$/,
114+
loaders: [
115+
'style',
116+
'css-loader?modules',
117+
'postcss-loader',
118+
],
119+
},
120+
],
121+
},
122+
123+
plugins: [
124+
new webpack.HotModuleReplacementPlugin(),
125+
//activates HMR
126+
127+
new webpack.NamedModulesPlugin(),
128+
//prints more readable module names in the browser console on HMR updates
129+
],
130+
}
131+
};
132+
```
133+
134+
There's a lot going on above, and not all of it is related to HMR.
135+
You may benefit from reading the
136+
[full documentation](https://webpack.github.io/docs/webpack-dev-server.html)
137+
on webpack dev server, and the [other articles](https://webpack.github.io/webpack.io/concepts/)
138+
here on webpack.io.
139+
140+
The basic assumption here is that your JS entry is located at `./src/index.js`,
141+
and that you're using CSS Modules for your styling.
142+
143+
Please see the comments inline that explain each portion of the config. The main
144+
areas to look are the `devServer` key and the `entry` key. The `HotModuleReplacementPlugin` is
145+
also necessary to include in the `plugins` array.
146+
147+
There are two modules included here for the purposes of this guide.
148+
The react-hot-loader addition to the entry, as noted above, is necessary to enable
149+
HMR with React components. The NamedModulesPlugin is a useful addition
150+
to better understand what modules are being updated when using HMR.
151+
152+
###Code
153+
In this guide, we're using the following files:
154+
155+
```js
156+
// ./src/index.js
157+
import React from 'react';
158+
import ReactDOM from 'react-dom';
159+
160+
import { AppContainer } from 'react-hot-loader'
161+
// AppContainer is a necessary wrapper component for HMR
162+
163+
import App from './components/App';
164+
165+
const render = () => {
166+
ReactDOM.render(
167+
<AppContainer>
168+
<App/>
169+
</AppContainer>,
170+
document.getElementById('root')
171+
);
172+
};
173+
174+
render();
175+
176+
// Hot Module Replacement API
177+
if (module.hot) {
178+
module.hot.accept('./components/App', render);
179+
}
180+
181+
182+
// ./src/components/App.js
183+
import React from 'react';
184+
import styles from './App.css';
185+
186+
const App = () => (
187+
<div className={styles.app}>
188+
<h2>Hello, </h2>
189+
</div>
190+
);
191+
192+
export default App;
193+
```
194+
195+
```css
196+
// ./src/components/App.css
197+
.app {
198+
text-size-adjust: none;
199+
font-family: helvetica, arial, sans-serif;
200+
line-height: 200%;
201+
padding: 6px 20px 30px;
202+
}
203+
```
204+
205+
The important thing to note in the code above is the `module` reference.
206+
First, we wrap the HMR code inside of `module.hot` check;
207+
webpack exposes `module` to the code, and if we are running with `hot: true` configured,
208+
we'll enter the inside of the conditional.
209+
210+
While the module API offers more options than what's above, the most
211+
important element is the `module.hot.accept` call.
212+
It specific how to handle changes to specific dependencies.
213+
214+
So in this case, `module.hot` will fire the `render` method ONLY
215+
when `src/components/App.js` changes! Note that would also include when the
216+
dependencies of `App.js` change -
217+
so the `render` method will file not just for changes made directly to the
218+
source of `App.js`, but also changes made to `App.css`, since `App.css`
219+
is included in `App.js`.
220+
221+
###Package.json
222+
Finally, we need to start up webpack dev server to bundle our code and see HMR in action.
223+
We can use the following package.json entry:
224+
225+
```js
226+
"start" : "webpack-dev-server --env.dev",
227+
```
228+
229+
Run `npm start`, open up your browser to `localhost:8080`,
230+
and you should see the folling entries printed in your console.log:
231+
232+
```
233+
dev-server.js:49[HMR] Waiting for update signal from WDS...
234+
only-dev-server.js:74[HMR] Waiting for update signal from WDS...
235+
client?c7c8:24 [WDS] Hot Module Replacement enabled.
236+
```
237+
238+
Go ahead and edit and save your App.js file.
239+
You should see something like the following in your console.log:
240+
241+
```
242+
[WDS] App updated. Recompiling...
243+
client?c7c8:91 [WDS] App hot update...
244+
dev-server.js:45 [HMR] Checking for updates on the server...
245+
log-apply-result.js:20 [HMR] Updated modules:
246+
log-apply-result.js:22 [HMR] - ./components/App.js
247+
dev-server.js:27 [HMR] App is up to date.
248+
```
249+
Note that HMR specifies the paths of the updated modules.
250+
That's because we're using the NamedModules plugin!
251+
252+

0 commit comments

Comments
 (0)