I've proposed upgrading —lucky of me— the React version of our project from v17.0.2 to v18.x.
While the npm i
is done fine, I have the problem that when I want to import the createRoot
method from react-dom/client
I get the error:
Module not found: Error: Can't resolve 'react-dom/client' in 'My-local-path'
If I don't import this module, the app correctly compiles, however, if I do, even if I don't use createRoot
, I get the error.
appLoader.js (index.js)
import React from 'react';
import ReactDOM from 'react-dom';
import { MemoryRouter, Route } from 'react-router-dom';
// import { createRoot } from 'react-dom/client'; // this breaks the app
// this works fine
ReactDOM.render(
<React.StrictMode>
<MemoryRouter>
<Route component={() => 'hello world'} />
</MemoryRouter>
</React.StrictMode>,
document.getElementById('root')
);
// const container = document.getElementById('root');
// if (container) {
// createRoot(container).render(
// <React.StrictMode>
// <MemoryRouter>
// <Route component={() => 'hello world'} />
// </MemoryRouter>
// </React.StrictMode>
// );
// }
I suspect that this has to do with Webpack. I saw here that it might be related of not including the module in Webpack.
We don't use Jest, therefore this solution won't apply.
- Also tried adding
externals
to webpack like commented here - I've tried deleting
package-log.json
then removingnode_modules
and reinstalling. - Played with several options in babel.config.js
- Wrapping all the components with
React.StrictMode
My webpack.config.js
require('env2')('./.env');
const _ = require('lodash');
const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const WebpackAssetsManifest = require('webpack-assets-manifest');
const isDev = process.env.NODE_ENV !== 'production';
const ifDev = (then) => (isDev ? then : null);
const ifProd = (then) => (!isDev ? then : null);
module.exports = {
target: isDev ? 'web' : ['web', 'es5'],
profile: true,
mode: isDev ? 'development' : 'production',
entry: {
sso: [
'./sso/publicPath',
ifDev('webpack-hot-middleware/client?dynamicPublicPath=true'),
ifDev('react-hot-loader/patch'),
'./sso/appLoader',
].filter(_.identity),
},
optimization: {
runtimeChunk: isDev,
minimize: !isDev,
minimizer: [
new TerserPlugin({
// fix bug "cannot declare const twice..." in Safari 10
minify: TerserPlugin.uglifyJsMinify,
terserOptions: {
mangle: { safari10: true },
toplevel: true,
output: { comments: false },
},
extractComments: false,
}),
new CssMinimizerPlugin(),
],
},
performance: { hints: false },
context: path.resolve(__dirname, './src'),
devtool: false,
output: {
publicPath: '/',
path: path.resolve(__dirname, './dist'),
filename: isDev ? '[name].bundle.js' : '[name].bundle.[contenthash].js',
},
// tried this
// externals: {
// 'react': { commonjs: 'react', commonjs2: 'react', amd: 'react', root: 'React' },
// 'react-dom': { commonjs: 'react-dom', commonjs2: 'react-dom', amd: 'react-dom', root: 'ReactDOM' },
// },
resolve: {
fallback: {
crypto: require.resolve('crypto-browserify'),
path: require.resolve('path-browserify'),
stream: require.resolve('stream-browserify'),
},
modules: [
path.resolve(__dirname, './src'),
path.resolve(__dirname, './assets'),
'node_modules',
],
alias: {
'@': path.resolve(__dirname, './src'), // include your file like this in less files: ~@/yourFile.less
'../../theme.config$': path.join(
__dirname,
'./src/theme/semantic/theme.config.less'
), // semantic requirement
'react-dom': isDev ? '@hot-loader/react-dom' : 'react-dom',
},
},
plugins: [
new webpack.ProvidePlugin({ process: 'process/browser' }),
ifDev(
new webpack.SourceMapDevToolPlugin({
filename: '[file].map',
exclude: /node_modules/,
})
),
ifProd(
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [
'**/*',
path.join(process.cwd(), 'logs/**/*'),
],
verbose: true,
})
),
ifProd(new webpack.LoaderOptionsPlugin({ minimize: true, debug: false })),
new MomentLocalesPlugin(),
ifDev(new webpack.HotModuleReplacementPlugin()),
new WebpackAssetsManifest({
publicPath: true,
writeToDisk: true,
entrypoints: true,
output: '../rendering-manifest.json',
}),
new MiniCssExtractPlugin({
filename: isDev ? '[name].css' : '[name].bundle.[contenthash].css',
}),
ifProd(
new CopyWebpackPlugin({
patterns: [{ from: path.resolve(__dirname, './assets/static') }],
})
),
].filter(_.identity),
module: {
rules: [
{
test: /\.js$/,
include: [
path.resolve(__dirname, './src'),
ifProd(path.resolve(__dirname, './node_modules/joi')),
].filter(_.identity),
use: 'babel-loader',
},
{
test: /\.(css|less)$/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{
loader: 'css-loader',
options: { importLoaders: 1, sourceMap: isDev },
},
{ loader: 'less-loader', options: { sourceMap: isDev } },
],
},
{
test: /\.jpe?g$|\.gif$|\.png$|\.ico$|\.ttf$|\.eot$|\.svg$|\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: [
{
loader: 'file-loader',
options: {
esModule: false,
name: isDev ? '[name].[ext]' : '[name].[contentHash].[ext]',
},
},
],
},
],
},
};
My babel.config.js
module.exports = {
sourceType: process.env.NODE_ENV === 'production' ? 'unambiguous' : 'module',
presets: [
['@babel/preset-react', { 'runtime': 'automatic' }],
['@babel/preset-env', {
loose: true,
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true },
modules: false,
targets: { browsers: process.env.NODE_ENV === 'production' ? 'chrome 53, safari 10, >0.1%' : 'chrome 86, safari 10' },
}],
],
plugins: [
['@babel/plugin-proposal-class-properties', { loose: true }],
'react-hot-loader/babel',
],
};
My partial package.json
"devDependencies": {
"@babel/core": "^7.12.9",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/preset-env": "^7.12.7",
"@babel/preset-react": "^7.12.7",
"@hot-loader/react-dom": "^17.0.1",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"classnames": "^2.2.6",
"clean-webpack-plugin": "^3.0.0",
"copy-webpack-plugin": "^6.3.2",
"core-js": "^3.8.0",
"cross-env": "^7.0.3",
"crypto-browserify": "^3.12.0",
"css-loader": "^5.0.1",
"css-minimizer-webpack-plugin": "^1.1.5",
"downscale": "^1.0.6",
"eslint": "^7.14.0",
"eslint-import-resolver-node": "^0.3.4",
"eslint-import-resolver-webpack": "^0.13.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.2.0",
"file-dialog": "0.0.8",
"file-loader": "^6.2.0",
"jwt-decode": "^3.1.2",
"less": "^3.12.2",
"less-loader": "^7.1.0",
"mini-css-extract-plugin": "^1.3.1",
"moment-duration-format": "^2.3.2",
"moment-locales-webpack-plugin": "^1.2.0",
"patch-package": "^6.2.2",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"prop-types": "^15.7.2",
"react-day-picker": "^7.4.8",
"react-helmet": "^6.1.0",
"react-hot-loader": "^4.13.0",
"react-rangeslider": "^2.2.0",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-virtualized": "^9.22.3",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"reselect": "^4.0.0",
"search-parser": "^0.1.19",
"semantic-ui-less": "^2.4.1",
"semantic-ui-react": "^2.0.1",
"stream-browserify": "^3.0.0",
"sweetalert": "^2.1.2",
"tachyons": "^4.12.0",
"terser-webpack-plugin": "^5.0.3",
"webpack": "^5.9.0",
"webpack-assets-manifest": "^3.1.1",
"webpack-cli": "^4.2.0",
"webpack-dev-middleware": "^4.0.2",
"webpack-hot-middleware": "^2.25.0",
"xlsx": "^0.16.9"
},
"dependencies": {
"@azure/storage-blob": "^12.3.0",
"@supercharge/promise-pool": "^1.6.0",
"applicationinsights": "^1.8.10",
"compression": "^1.7.4",
"cors": "^2.8.5",
"env2": "^2.2.2",
"express": "^4.17.1",
"fast-xml-parser": "^3.17.5",
"install": "^0.13.0",
"joi": "^17.3.0",
"jsonwebtoken": "^8.5.1",
"jwk-to-pem": "^2.0.4",
"knex": "^0.21.12",
"locutus": "^2.0.14",
"lodash": "^4.17.20",
"mjml": "^4.7.1",
"moment": "^2.29.1",
"moment-timezone": "^0.5.32",
"react-dom": "^18.2.0",
"react": "^18.2.0",
"mssql": "^6.2.3",
"nanoid": "^3.1.20",
"node-cache": "^5.1.2",
"node-fetch": "^2.6.1",
"nodemailer": "^6.4.16",
"npm": "^8.13.2",
"parse": "^2.17.0",
"parse-dashboard": "^2.1.0",
"parse-server": "^4.4.0",
"query-string": "^6.13.7",
"react-sortable-hoc": "^1.11.0",
"redis": "^4.2.0",
"saslprep": "^1.0.3",
"sharp": "^0.26.3",
"uuid": "^8.3.1"
}
CodePudding user response:
The issue is that this project is use the dependency @hot-loader/react-dom
, and it was overriding react-dom
.
module.exports = {
resolve: {
alias: {
'@': path.resolve(__dirname, './src'), // include your file like this in less files: ~@/yourFile.less
'../../theme.config$': path.join(
__dirname,
'./src/theme/semantic/theme.config.less'
), // semantic requirement
// 'react-dom': isDev ? '@hot-loader/react-dom' : 'react-dom', // this was overriding the react-dom version in node_modules
},
},
};
By the way, the aforementhioned dependency has been deprecated, so if you want to upgrade React you'll have to find an alternative like react-refresh-webpack-plugin