最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Webpack Hot module replacement, react 18 ReactDOMClient.createRoot() on a container that has already been passed to

programmeradmin1浏览0评论

I updated React to v18 and my Webpack dev server gives me a console error whenever the hot module replacement fires and injects the new javascript code:

Warning: You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root. render() on the existing root instead if you want to update it.

index.js file

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);

root.render(<App />);

if (module.hot) module.hot.accept(function (err) {
    console.log('An error occurred while accepting new version');
});

webpack.config.js

const path = require('path');
const HtmlWEbpackPlugin = require('html-webpack-plugin');

module.exports = (env) => {
  let cfg = {
    mode: 'development',
    entry: './src/index.js',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'bundle.[contenthash:6].js',
      publicPath: '/',
      clean: true
    },
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader'
          }
        },
      ]
    },
    plugins: [new HtmlWEbpackPlugin({ template: './src/index.html' })
    ],
    devServer: {
      static: {
        directory: path.join(__dirname, 'public'),
      },
      compress: true,
      port: 3000,
      hot: true,
      open: true,
      
    },
    performance: {
      hints: false
    }

  }

  return cfg;
};

I updated React to v18 and my Webpack dev server gives me a console error whenever the hot module replacement fires and injects the new javascript code:

Warning: You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root. render() on the existing root instead if you want to update it.

index.js file

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.jsx';

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);

root.render(<App />);

if (module.hot) module.hot.accept(function (err) {
    console.log('An error occurred while accepting new version');
});

webpack.config.js

const path = require('path');
const HtmlWEbpackPlugin = require('html-webpack-plugin');

module.exports = (env) => {
  let cfg = {
    mode: 'development',
    entry: './src/index.js',
    output: {
      path: path.resolve(__dirname, 'dist'),
      filename: 'bundle.[contenthash:6].js',
      publicPath: '/',
      clean: true
    },
    module: {
      rules: [
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader'
          }
        },
      ]
    },
    plugins: [new HtmlWEbpackPlugin({ template: './src/index.html' })
    ],
    devServer: {
      static: {
        directory: path.join(__dirname, 'public'),
      },
      compress: true,
      port: 3000,
      hot: true,
      open: true,
      
    },
    performance: {
      hints: false
    }

  }

  return cfg;
};
Share Improve this question edited Apr 4, 2022 at 15:39 Tural Asgarov 1,3772 gold badges14 silver badges27 bronze badges asked Apr 2, 2022 at 16:16 ptmvaptmva 1311 gold badge2 silver badges5 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 21

React 18 has native support for hot reloading, this is called Fast Refresh. An excerpt from the react-refresh readme:

Fast Refresh is a feature that lets you edit React components in a running application without losing their state. It is similar to an old feature known as "hot reloading", but Fast Refresh is more reliable and officially supported by React.

To use this with Webpack 5, you will need a plugin called react-refresh-webpack-plugin. To get it to work I would recommend taking a look at the examples included in the git repository, especially the webpack-dev-server example.

NOTE: As of writing this answer, the react-refresh-webpack-plugin is in an experimental state but create-react-app is already using it, so it is probably stable enough to use.

The following is taken straight from react-refresh-webpack-plugin's webpack-dev-server example:

src/index.js

import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App />);

webpack.config.js

const path = require('path');
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const isDevelopment = process.env.NODE_ENV !== 'production';

module.exports = {
  mode: isDevelopment ? 'development' : 'production',
  devServer: {
    client: { overlay: false },
  },
  entry: {
    main: './src/index.js',
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/,
        include: path.join(__dirname, 'src'),
        use: 'babel-loader',
      },
    ],
  },
  plugins: [
    isDevelopment && new ReactRefreshPlugin(),
    new HtmlWebpackPlugin({
      filename: './index.html',
      template: './public/index.html',
    }),
  ].filter(Boolean),
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};

babel.config.js

module.exports = (api) => {
  // This caches the Babel config
  api.cache.using(() => process.env.NODE_ENV);
  return {
    presets: [
      '@babel/preset-env',
      // Enable development transform of React with new automatic runtime
      ['@babel/preset-react', { development: !api.env('production'), runtime: 'automatic' }],
    ],
    // Applies the react-refresh Babel plugin on non-production modes only
    ...(!api.env('production') && { plugins: ['react-refresh/babel'] }),
  };
};

You can remove the following from your Webpack entry point:

src/index.js

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

This has the small drawback that whenever you modify your entry point (src/index.js) a full reload is necessary. Webpack is very in your face about needing to do a full reload, showing you the following log messages.

This really annoyed me. When looking at how create-react-app solved this, I found that they disabled client side logging for the webpack-dev-server, or at least set the log level to warn or error. You can set the log level by setting the client.logging property in the devServer configuration:

webpack.config.js

// ...
devServer: {
  client: {
    overlay: false,
    logging: 'warn' // Want to set this to 'warn' or 'error'
  }
}
// ...

What the odd thing is about the "warning", is that it is not a warning at all, it is just an info message dressed up as a warning.

Hope this helps.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论