Search code examples
djangoreactjswebpack-dev-serverwebpack-hmrreact-hot-loader

django webpack loader: React app hot reload failure


Some context: I am developing a Django application, and I would like to integrate a React component in a template. I am new to React, not so to Django.

So, I am trying to setup a React dev environment, with hot reloading of the React component inside my Django template. I followed these tutorials :

And it seems quite fine, since I have the following feedbacks when I modify and save my js source code:

in npm console:

> node server.js

Listening at 0.0.0.0:3000
Hash: ecfef9f1eea0022319ef
Version: webpack 3.3.0
Time: 6294ms
                       Asset     Size  Chunks                    Chunk Names
main-ecfef9f1eea0022319ef.js  1.37 MB       0  [emitted]  [big]  main
  [26] ./node_modules/react/react.js 56 bytes {0} [built]
  [77] (webpack)/hot/log.js 1.04 kB {0} [built]
 [141] (webpack)/hot/emitter.js 77 bytes {0} [built]
 [142] ./node_modules/react-dom/index.js 59 bytes {0} [built]
 [169] ./assets/js/App.js 779 bytes {0} [built]
 [170] multi react-hot-loader/patch webpack-dev-server/client?http://localhost:3000 webpack/hot/only-dev-server ./assets/js/index.js 64 bytes {0} [built]
 [171] ./node_modules/react-hot-loader/patch.js 41 bytes {0} [built]
 [172] ./node_modules/react-hot-loader/lib/patch.js 209 bytes {0} [built]
 [293] (webpack)-dev-server/client?http://localhost:3000 5.83 kB {0} [built]
 [302] ./node_modules/loglevel/lib/loglevel.js 6.74 kB {0} [built]
 [335] (webpack)-dev-server/client/overlay.js 3.6 kB {0} [built]
 [340] (webpack)/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
 [342] (webpack)/hot/only-dev-server.js 2.37 kB {0} [built]
 [344] ./assets/js/index.js 1.02 kB {0} [built]
 [430] ./node_modules/react-hot-loader/index.js 41 bytes {0} [built]
    + 430 hidden modules
webpack: Compiled successfully.
webpack: Compiling...
Hash: 8fd43db8b836382b9172
Version: webpack 3.3.0
Time: 206ms
                               Asset       Size  Chunks                    Chunk Names
        main-8fd43db8b836382b9172.js    1.37 MB       0  [emitted]  [big]  main
0.ecfef9f1eea0022319ef.hot-update.js  892 bytes       0  [emitted]         main
ecfef9f1eea0022319ef.hot-update.json   43 bytes          [emitted]
  [26] ./node_modules/react/react.js 56 bytes {0}
  [77] (webpack)/hot/log.js 1.04 kB {0}
 [141] (webpack)/hot/emitter.js 77 bytes {0}
 [142] ./node_modules/react-dom/index.js 59 bytes {0}
 [169] ./assets/js/App.js 780 bytes {0} [built]
 [170] multi react-hot-loader/patch webpack-dev-server/client?http://localhost:3000 webpack/hot/only-dev-server ./assets/js/index.js 64 bytes {0}
 [171] ./node_modules/react-hot-loader/patch.js 41 bytes {0}
 [172] ./node_modules/react-hot-loader/lib/patch.js 209 bytes {0}
 [293] (webpack)-dev-server/client?http://localhost:3000 5.83 kB {0}
 [302] ./node_modules/loglevel/lib/loglevel.js 6.74 kB {0}
 [335] (webpack)-dev-server/client/overlay.js 3.6 kB {0}
 [340] (webpack)/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
 [342] (webpack)/hot/only-dev-server.js 2.37 kB {0}
 [344] ./assets/js/index.js 1.02 kB {0}
 [430] ./node_modules/react-hot-loader/index.js 41 bytes {0}
    + 430 hidden modules
webpack: Compiled successfully.

And in the react-dev-tools console I get:

[HMR] Waiting for update signal from WDS...  main-ecfef9f1eea0022319ef.js:8192:4
[WDS] Hot Module Replacement enabled.  main-ecfef9f1eea0022319ef.js:22302:3
[WDS] App updated. Recompiling... main-ecfef9f1eea0022319ef.js:22305:3
[WDS] App hot update...  main-ecfef9f1eea0022319ef.js:22431:3
[HMR] Checking for updates on the server...  main-ecfef9f1eea0022319ef.js:8192:4
[HMR] Updated modules:  main-ecfef9f1eea0022319ef.js:8192:4
[HMR]  - 169  main-ecfef9f1eea0022319ef.js:8192:4
[HMR] Consider using the NamedModulesPlugin for module names.  main-ecfef9f1eea0022319ef.js:8192:4
[HMR] App is up to date.

Which looks pretty good, yet my React component on my django page stays desperatly the same, unless I hit F5 of course. I guess I'm missing something obvious probably, I would appreciate any helpful pointer.

Here is my setup:

package.json (deps only):

"devDependencies": {
    "babel": "^6.23.0",
    "babel-core": "^6.0.0",
    "babel-loader": "^7.1.1",
    "babel-preset-env": "^1.6.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "react": "^15.6.1",
    "react-dom": "^15.6.1",
    "react-hot-loader": "^3.0.0-beta.7",
    "webpack": "^3.3.0",
    "webpack-bundle-tracker": "^0.2.0",
    "webpack-dev-server": "^2.6.1"
  }

.babelrc:

{
    "presets": ["env", "es2015", "react"],
    "plugins": ["react-hot-loader/babel"]
}

webpack.config.js:

var path = require("path")
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')

const config = {
  context: __dirname,

  entry: [
    'react-hot-loader/patch',
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './assets/js/index.js', // entry point of our app. assets/js/index.js should require other js modules and dependencies it needs
  ],

  output: {
      path: path.resolve('./assets/bundles/'),
      filename: "[name]-[hash].js",
      publicPath: 'http://localhost:3000/assets/bundles/' // Tell django to use this URL to load packages and not use STATIC_URL + bundle_name
  },

  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoEmitOnErrorsPlugin(), // don't reload if there is an error
    new BundleTracker({filename: './webpack-stats.json'}),
  ],

  module: {
    rules: [
      // we pass the output from babel loader to react-hot loader
      { test: /\.jsx?$/, exclude: /node_modules/, use: ['babel-loader'], },
    ],
  },

  resolve: {
    modules: ['node_modules', 'bower_components'],
    extensions: ['.js', '.jsx']
  },

  devServer: {
    hot: true,
    contentBase: './assets/js',
  }
}

module.exports = config;

server.js:

var webpack = require('webpack')
var WebpackDevServer = require('webpack-dev-server')
var config = require('./webpack.config')

new WebpackDevServer(webpack(config), {
    publicPath: config.output.publicPath,
    hot: true,
    inline: true,
    historyApiFallback: true,
    headers: {
        'Access-Control-Allow-Origin': '*',
    },
}).listen(3000, '0.0.0.0', function(err, result) {
    if (err) {
        console.log(err)
    }

    console.log('Listening at 0.0.0.0:3000')
})

assets/js/index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import App from './App'


const render = Component => {
    ReactDOM.render( 
        <AppContainer>
        <Component />
        </AppContainer>,
        document.getElementById('react-app')
    )
}
render(App)
if (module.hot) {
    module.hot.accept('./App', () => { render(App) })
}

assets/js/App.js:

import React from 'react';

const App = () => {
return (
    <div>
    <h1>Hello StackOverflow</h1>
    </div>
    );
}
export default App;

django settings.py:

WEBPACK_LOADER = {
    'DEFAULT': {
        'CACHE': not DEBUG,
        'BUNDLE_DIR_NAME': 'bundles/',
        'STATS_FILE': os.path.join(BASE_DIR, 'webpack-stats.json'),
        'POLL_INTERVAL': 0.1,
        'TIMEOUT': None,
        'IGNORE': ['.+\.hot-update.js', '.+\.map']
    }
}

django template:

{% load render_bundle from webpack_loader %}

{% block content-right %}
    <div id="react-app"></div>
    {% render_bundle 'main' %}
{% endblock content-right %}

Solution

  • In your index.js

    Instead of:

    if (module.hot) {
        module.hot.accept('./App', () => { render(App) })
    }
    

    Use:

    if(module.hot) {
        module.hot.accept();
    }
    

    there is an issue in github : https://github.com/webpack/webpack-dev-server/issues/100

    or by changing the Babel ES2015 preset to be ["es2015", { "modules": false }]

    Can't get Webpack 2 HMR React to work