Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting webpack hot updating to work correctly in my isomorphic web app

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.

like image 208
Øyvind Bråthen Avatar asked Feb 09 '16 20:02

Øyvind Bråthen


People also ask

How do I enable hot reload in Webpack?

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.

How does Webpack hot module replacement work?

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.


1 Answers

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:

The babel settings

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.

The loader

{ 
    test: /\.(js|jsx)$/,
    loaders: ['babel?' + JSON.stringify(babelSettings)],
    exclude: /node_modules/
}
like image 157
Øyvind Bråthen Avatar answered Sep 29 '22 22:09

Øyvind Bråthen