react-refresh-webpack-plugin: First recompile triggers a full page reload

Hello and thanks for an awesome plugin. it would be really useful for @openshift/console because of our massive websockets use (with this plugin there are no full page reloads so the websockets remain intact!)

  1. we use Typescript -> webpack.config.ts
  2. we use ForkTsCheckerWebpackPlugin for type checking.

I followed all suggested actions:

  1. peer dependency type-fest
  2. tried to learn from this sample (got it from README.md)

It seems to work wonderfully! But the first recompilation after a code change triggers a full page reload, do we happen to know why it happens?

After some troubleshooting, I managed to finish with the following config -

// DEV SERVER config
  devServer: {
    writeToDisk: true,
    progress: true,
    hot: true,
    inline: true,
  },

Pastebin of the full webpack file

{
        test: /(\.jsx?)|(\.tsx?)$/,
        exclude: /node_modules\/(?!(bitbucket|ky)\/)/,
        use: [
          { loader: 'cache-loader' },
          {
            loader: 'thread-loader',
            options: {
              // Leave one core spare for fork-ts-checker-webpack-plugin
              workers: require('os').cpus().length - 1,
            },
          },
// THIS IS NEW, ALL THE REST WERE PRESENT
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-env'],
              plugins: [
                '@babel/plugin-syntax-dynamic-import',
                [
                  '@babel/plugin-transform-runtime',
                  {
                    regenerator: true,
                  },
                ],
                isDevelopment && 'react-refresh/babel',
              ],
              ignore: ['node_modules/bitbucket'],
            },
          },
//
          {
            loader: 'ts-loader',
            options: {
              // Always use the root tsconfig.json. Packages might have a separate tsconfig.json for storybook.
              configFile: path.resolve(__dirname, 'tsconfig.json'),
              happyPackMode: true, // This implicitly enables transpileOnly! No type checking!
              transpileOnly: true, // fork-ts-checker-webpack-plugin takes care of type checking
            },
          },
        ],
      },

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 21 (10 by maintainers)

Most upvoted comments

Criteria for full refresh are not as obvious as you might think, because HMR updates can bubble/propagate up the dependency chain.

For fast refresh, we will only stop bubbling if and only if we hit a refresh boundary, which is a module with React-related exports only.

With that said:

  1. An unnamed component that’s mounted has had its code changed.

If parent boundary exists - stop at boundary and reload the chain only Else, we will bubble to root and bail out.

  1. An unnamed component that’s not mounted has had its code changed.

Whether a component is mounted does not matter for HMR theoretically. What matters is if the component is “used” in ES6 modules sense (because if it’s not used, it will be tree-shaken).

Let’s say it is unused - then the app will not bail out cause the component is not used via any hot update chains.

  1. An unnamed component that’s mounted hasn’t had its code changed (and some other component’s code has changed).

Again, this depends on the relationship of the other component to the unnamed component. If the other component hits a boundary (itself can also be one) before we even propagates to the unnamed component, we won’t bail out. Else, we will.

  1. An unnamed component that’s not mounted hasn’t had its code changed (and some other component’s code has changed).

Reference to 2 and 3, with the same reasoning most likely this won’t trigger bail out behaviour.

We experienced something similiar. Changing to

 devServer: {
    hotOnly: true,
    ...
  }

seems to fix it.