renchap/webpacker-react

HMR not working

RobinClowers opened this issue · 20 comments

I followed the steps in the readme, and I see the correct console output in my app, but my components don't actually re-render. Here is the output I see in the console:

[HMR] Waiting for update signal from WDS...
components.js:56356 [WDS] Hot Module Replacement enabled.
components.js:56356 [WDS] App updated. Recompiling...
components.js:56356 [WDS] App hot update...
components.js:56789 [HMR] Checking for updates on the server...
components.js:56832 [HMR] Updated modules:
components.js:56834 [HMR]  - ./app/javascript/components/SearchBar.js
components.js:56771 [HMR] App is up to date.

When I watch the dom inspector, I see the new script tag added to the head element, but nothing else in the dom changes. Any suggestions on how to debug this?

EDIT: I updated webpacker (to 1.2) and tried this again, and now I have a different problem. My app requests http://localhost:8080/ba9cba921e647214acee.hot-update.json, but the dev server generated 0.ba9cba921e647214acee.hot-update.js. Any idea how to fix that?

Scratch that, I realize now I was confusing the hot-update.json with the actual js chunk (which has a chunk version prepended, in this case 0). The wepback output says it generated ba9cba921e647214acee.hot-update.json, but http://localhost:8080/ba9cba921e647214acee.hot-update.json is a 404. http://localhost:8080/packs/ba9cba921e647214acee.hot-update.json is valid, so why the mismatch? Should the public path include /packs?

Ok, so I updated my publicPath to http://localhost:8080/packs/, and now I see this when I change a component:

[HMR] Updated modules:
[HMR]  - ./app/javascript/components/MyComponent.js

But it the change doesn't actually show up.

I dont see anything wrong here. Can you show the content of your pack file? Are you correctly calling module.hot.accept with the correct path, callback and module?

Here is my pack file:

import SearchBar from 'components/SearchBar'
import WebpackerReact from 'webpacker-react/hmr'
import 'closestPolyfill'
import 'babel-polyfill'
import 'whatwg-fetch'

WebpackerReact.setup({
  SearchBar,
})

if (module.hot) {
  module.hot.accept('components/SearchBar', () => WebpackerReact.renderOnHMR(SearchBar))
}

Which Webpacker version are you using? Webpacker's configuration changed quite a lot recently and I suspect the issue can come from here.
Can you change your Webpack config to output the configuration and show it here?

In config/webpack/development.server.js (or the config file used by webpack-dev-server in your setup), do something like:

config = merge(devConfig, { ... } )
console.log(config);
module.exports = config

I'm on webpacker 1.2, this is what my config looks like:

{ entry:
   { components:
      [ 'react-hot-loader/patch',
        '/<project-path>/app/javascript/packs/components.js.erb' ] },
  output:
   { filename: '[name].js',
     path: '/<project-path>/public/packs',
     publicPath: 'http://localhost:8080/packs/',
     pathinfo: true },
  module: { rules: [ [Object], [Object], [Object], [Object], [Object] ] },
  plugins:
   [ EnvironmentPlugin { keys: [Object], defaultValues: [Object] },
     ExtractTextPlugin { filename: '[name].css', id: 1, options: {} },
     ManifestPlugin { opts: [Object] },
     NamedModulesPlugin { options: {} } ],
  resolve:
   { extensions:
      [ '.coffee',
        '.js',
        '.jsx',
        '.ts',
        '.vue',
        '.sass',
        '.scss',
        '.css',
        '.png',
        '.svg',
        '.gif',
        '.jpeg',
        '.jpg' ],
     modules:
      [ '/<project-path>/app/javascript',
        '/<project-path>/node_modules' ] },
  resolveLoader: { modules: [ 'node_modules' ] },
  stats: { errorDetails: true } }

One difference I see it with my current setup is in entry.components: for you the full path is used, but in my config only the relative path (components.js.erb) is used. As /<project-path>/app/javascript is in resolve.modules it is not needed to have the full path in entry, and I think it can cause an issue with hot reloading as it may not match the component path you use in module.hot.accept

Another difference is that I only use plain .js packs, I dont know if this can make a difference.

Could you try to change the Webpack config with a static entry and see if it works?

{
  entry: {
    components: ['packs/components'],
  }
}

@renchap thanks for the suggestions. I dropped the erb extension to make sure that wasn't the issue, and I tried the static entry, which doesn't seem to change the situation.

You mentioned the entry path has to match the path in hot.accept, which it doesn't. From my console output: components.js:56834 [HMR] - ./app/javascript/components/SearchBar.js, but the entry path is packs/components or /<project-path>/app/javascript/packs/components.js.

I've run into exactly the same issue.

From my entry:

'orders/summary':
      [ 'react-hot-loader/patch',
        '<Rails root>/app/javascript/packs/orders/summary.jsx' ],

From console log:

[WDS] App hot update...
[HMR] Checking for updates on the server...
[HMR] Updated modules:
[HMR]  - ./app/javascript/packs/orders/summary.jsx
[HMR] App is up to date.

I'm accepting orders/order_form which (indirectly) imports the summary file.

However, the changes don't appear on the page when hot reloading occurs.

This looks like to be related with recent changes to Webpacker.
I updated my main application to Webpacker 2.0 and I am seeing the publicPath issue you mentioned in the first post.

I am looking at this and will try to reproduce and fix.

I got HMR working with the latest Webpacker (from Git master, but it should be the same for Webpacker 2.0).
Your fix for the publicPath issue is not correct. The problem comes from the way webpacker configures it. It is used at 2 places: in the manifest plugin configuration, and in the dev server config.
Webpacker uses manifest.json to know what is the URL for each pack file.

The real fix is to fix the URL in both places (manifest.json and dev server config).

For the lastest Webpack, I changed config/webpack/configuration.js to:

const output = {
  ...
  publicPath: '//localhost:8080/',
}

After restarting webpack-dev-server, public/packs/manifest.json contains the correct URLs for the packfiles (http://localhost:8080/pack.js, without /packs prefix), and serves the assets correctly at this URL.

HMR is working fine with this setup.

Can you please test this change and confirm that it works? Warning: this forces publicPath, so it will no longer work on production. Wrap the change in an if NODE_END == 'development' statement if you want to deploy it.

I will open an issue in Webpacker repo to add dev server support for publicPath.

After trying to understand the real issue, the fix above only masked the problem.
The issue lies here: https://github.com/renchap/webpacker-react/blob/master/javascript/webpacker_react-npm-module/src/configure-hot-module-replacement.js#L11

webpacker now correctly sets output.publicPath, so it is no longer needed to override it. And the override was incorrect, it missed the /packs/ directory.

I just release version 0.3.0 with the bugfix (and some other changes).
Can you please test it and tell me if all works fine for you now?

I'm still having the second issue that came up (ie. the URL isn't the problem, but)

Ok, so I updated my publicPath to http://localhost:8080/packs/, and now I see this when I change a component:
[HMR] Updated modules:
[HMR] - ./app/javascript/components/MyComponent.js
But it the change doesn't actually show up.

The output looks correct but the components aren't actually updated.

Are you using Webpacker-React 0.3.0, both for the Gem and the NPM module?
Which version of Webpacker are you using?

Is it a change you could reproduce this behaviour with a simple Rails app and put it on GitHub so I can investigate?

Also, can you check that presets.env.modules is set to false in your Babel config? (.babelrc, which can be overridden in config/webpack/loaders/babel.js and config/webpack/loaders/react.js)

Using webpacker-react@0.3.0 for NPM, 0.3.1 for the Gem.

Webpacker 2.0, pretty sure modules is set to false. Output didn't change when I upgraded webpacker-react versions.

Will try to reproduce + minimize.

I was unable to reproduce in a new Rails app. Probably some odd, specific bug in my config.

You can consider this closed IMO 👍

I am interested to know which part of your configuration causes this, if you find out please comment here, it will surely help others and I will add it to the README.

I went through every webpack-related file and changed my app to the rails new app that had HMR working until I fixed my app.

I think I isolated the issue down to:

  • Removing "transform-es2015-modules-commonjs" from my .babelrc plugins.

Definitely my fault, hopefully posting this will help someone else in the future with the same problem.

Ok, as I thought it is linked to how modules are handled. This Babel plugin, like the preset I wrote before, transforms import statements and this breaks Webpack's handling of modules. As HMR is dependent on modules, it broke. This is why you only saw one module updated in your console, and not the whole chain of modules (from the root component to the updated component).