I'm creating a webapp with a node/express backend and a react frontend. I got (I think) most of it up and running, but the last step of getting the browser to perform a hot refresh does not work as intended. I'll try to post all the relevant setup here. Please let me know if you require anything else to find out where I have done a mistake:
I start up my application with node ./server/index.js
webpack.config.js var path = require('path'); var webpack = require('webpack');
let webpackConfig = {
name: 'server',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/dist/',
},
resolve: {
extensions: [
'', '.js', '.jsx', '.json'
]
},
module: {
loaders: [
{
test: /\.(js|jsx)$/,
loader: 'babel-loader',
exclude: /node_modules/,
query:{
presets: ['es2015', 'react', 'stage-2']
}
},
{
test: /\.json$/,
loader: 'json-loader'
}
]
},
entry: [
'webpack-hot-middleware/client',
'./app/client/client.jsx'
],
plugins: [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
};
export default webpackConfig;
index.js just include 'babel-register' and 'server.js'
server/server.js import webpack from 'webpack'; import webpackConfig from '../webpack.config'; import webpackDevMiddleware from 'webpack-dev-middleware'; import webpackHotMiddleware from 'webpack-hot-middleware';
import express from 'express';
const app = express();
const renderPage = () => {
return `
<!doctype html>
<html>
<head>
<title>Brewing Day</title>
<meta charset='utf-8'>
</head>
<body>
<h1>Hello from server.js!!</h1>
<div id='root'></div>
<script src='/dist/bundle.js'></script>
</body>
</html>
`;
};
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler, {
noInfo: true,
publicPath: webpackConfig.output.publicPath })
);
app.use(webpackHotMiddleware(compiler));
app.use((req, res, next) => {
res.status(200).end(renderPage());
});
const server = app.listen(3005, () => {
const host = server.address().address;
const port = server.address().port;
console.log(`Listening at http://${host}:${port}`);
})
export default server;
And app/client/client.jsx that is the entrypoint in the webpack config:
import React from 'react';
import ReactDOM from 'react-dom';
import Application from '../components/application.jsx';
window.addEventListener('load', () => {
ReactDOM.render(<Application />, document.getElementById('root')
);
});
In the console, when I fire it up, it lists the following line:
webpack built cc1194a11614a3ba54a3 in 730ms
When I do a change to for example client.jsx
or application.jsx
that contains the rect component, I get two new lines in my console:
webpack building...
webpack built 8d340a8853d3cfe9478d in 195ms
So far, so good!
However, in the browser, it does not update and gives the following warning in console:
[HMR] The following modules couldn't be hot updated: (Full reload needed)
This is usually because the modules which have changed (and their parents) do not know how to hot reload themselves. See http://webpack.github.io/docs/hot-module-replacement-with-webpack.html for more details.
[HMR] - ./app/components/application.jsx
I tried randomly adding module.hot.accept()
to application.jsx
. That get's rid of the warnings, but still no update without hitting F5 and reloading the browser.
Any idea what I'm missing here? I have seen another example set up almost like mine, where this works without any module.hot.accept()
calls anywhere, but I fail to see where my setup differ from the other setup.
Any and all help will be appreciated.
To enabling HMR in your project, you need to let your application know how to handle hot updates. You can do so by implementing the module. hot API exposed by Webpack. Once the hot update is accepted, the HMR runtime and the loaders will take over to handle the update.
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running, without a full reload. This can significantly speed up development in a few ways: Retain application state which is lost during a full reload. Save valuable development time by only updating what's changed.
After a lot of digging around, I found the answer to my problem. I was creating my base React "class" like this:
class Application = () => {
return (
<div id="maincontent">
<MainMenu />
<ScreenContents />
</div>
)
};
This is unsupported for HMR, even if it's supported by React.
I had to create my class explicitly like this:
class Application extends React.Component{
render (){
return (
<div id="maincontent">
<MainMenu />
<ScreenContents />
</div>
);
}
};
And then HMR works just fine :)
Edit: According to @akoskm comments, it seems that the babel configuration in the webpack configuration file might be an issue as well. So here are the relevant parts:
var babelSettings = {
presets: ['react', 'es2015', 'stage-2'],
env: {
development: {
plugins: [
['react-transform', {
transforms: [
{
transform: 'react-transform-hmr',
imports: [ 'react' ],
locals: [ 'module' ]
}
]
}]
]
}
}
};
The presets and environment stuff might not be exactly the same for you, but the react-transform
stuff is the important part here.
{
test: /\.(js|jsx)$/,
loaders: ['babel?' + JSON.stringify(babelSettings)],
exclude: /node_modules/
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With