Search code examples
reactjswebpackreact-hot-loader

Component not reloaded with react hot loader


https://github.com/James2516/hot-reload

I'm trying to add hot loader to an existing webpack project.

It detected file changes but did not reload the component.

To reproduce:

  1. yarn && yarn start
  2. change src/App.js content

Solution

  • The examples are taken from https://gaearon.github.io/react-hot-loader/getstarted/#step-2-of-3-using-hmr-to-replace-the-root-component, though it seems as though you already followed this guide.

    The first problem is that [HMR] Waiting for update signal from WDS... is printed to the console twice, and any attempted recompiles also occur twice. This is caused by your entry in ./config/webpack/Dev.js being:

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

    The first few steps of the guide have the entry point as:

    [
      'webpack-dev-server/client?http://0.0.0.0:3000', // WebpackDevServer host and port
      'webpack/hot/only-dev-server', // 'only' prevents reload on syntax errors
      './scripts/index' // Your app's entry point
    ]
    

    Step 3a under Step 3 (of 3): Adding React Hot Loader to preserve component state instructs you to modify the entry of webpack.config.js to look like:

    [
      'react-hot-loader/patch', // RHL patch
      './scripts/index' // Your app's entry point
    ]
    

    react-hot-loader handles what these two entries were previously doing, so they can be replaced with 'react-hot-loader/patch'.

    So your new entry point should be:

    [
      'react-hot-loader/patch',
      './client.js'
    ]
    

    Next, Step 3b in the same section has a code snippet that looks like:

    if (module.hot) {
      module.hot.accept('./containers/rootContainer.js', () => {
        const NextRootContainer = require('./containers/rootContainer').default;
        render(NextRootContainer);
      });
    }
    

    Changing your module.hot block to:

    if (module.hot) {
      module.hot.accept('./App', () => {
        const newApp = require('./App').default;
        render(newApp);
      }
    }
    

    Correctly reloads the component, so it doesn't make sense that following Step 3c breaks HMR.

    c. Webpack 2 has built-in support for ES2015 modules, and you won’t need to re-require your app root in module.hot.accept. The example above becomes:

    if (module.hot) {
      module.hot.accept('./containers/rootContainer', () => { 
       render(RootContainer) });
    }
    

    Note: To make this work, you’ll need to opt out of Babel transpiling ES2015 modules by changing the Babel ES2015 preset to be

    "presets": [["es2015", { "modules": false }]]

    Which you did correctly in .babelrc. The only problem is that in ./config/webpack/Base.js you have:

    loader: 'babel-loader',
    query: {
      presets: ['es2015', 'react', 'stage-2']
    }
    

    This causes babel-loader to use these presets instead of the ones defined in .babelrc. Removing query in this rule and changing presets in .babelrc to:

    "presets": [["es2015", { "modules": false }], "react", "stage-2"
    

    Correctly enables HMR with your module.hot block as:

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