'Webpack Hot module replacement, react 18 ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before
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;
};
Solution 1:[1]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | sxkx |

![[image]](https://i.stack.imgur.com/hMceS.png)