1

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.

  1. Also tried adding externals to webpack like commented here
  2. I've tried deleting package-log.json then removing node_modules and reinstalling.
  3. Played with several options in babel.config.js
  4. 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"
  }
3
  • Show dependencies sections as well. Especially react and react dom Commented Dec 1, 2022 at 22:58
  • 1
    Oh right, that's weird this project has react and react-dom in the dev deps. I'll update with the list. Commented Dec 1, 2022 at 23:02
  • Same results with react and react-dom in deps instead dev btw. Commented Dec 1, 2022 at 23:10

1 Answer 1

0

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.