Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Esbuild best practice for bundling React apps with deps triggering multiple React instances?? #3419

Closed
cameronelliott opened this issue Oct 3, 2023 · 5 comments

Comments

@cameronelliott
Copy link

I am using esbuild 0.19.3

What is happening:
In a simple React project that imports a library, which imports React:
React is getting imported twice as per this --analyze=verbose output

c@macmini ~/D/r/w/e/whep-viewer (main)> npx esbuild --bundle --outdir=www/js  src/index.tsx --analyze=verbose --servedir=www src/index.tsx --platform=browser

 > Local:   http://127.0.0.1:8000/
 > Network: http://192.168.86.135:8000/
 > Network: http://192.168.86.134:8000/


  www/js/index.js ──────────────────────────────────────────────────── 1.2mb ─ 100.0%
   ├ node_modules/react-dom/cjs/react-dom.development.js ─────────── 926.6kb ── 77.6%
   │  └ node_modules/react-dom/index.js
   │     └ node_modules/react-dom/client.js
   │        └ src/index.tsx
   ├ ../../node_modules/react/cjs/react.development.js ────────────── 78.9kb ─── 6.6%
   │  └ ../../node_modules/react/index.js
   │     └ ../../dist/WhepViewer.js
   │        └ ../../dist/index.js
   │           └ src/index.tsx
   ├ node_modules/react/cjs/react.development.js ──────────────────── 78.9kb ─── 6.6%
   │  └ node_modules/react/index.js
   │     └ node_modules/react/cjs/react-jsx-runtime.development.js
   │        └ node_modules/react/jsx-runtime.js
   │           └ src/index.tsx

Modern React with hooks simply cannot work correctly when multiple instances are created

link explaining WHY you can't have multiple instances of React in an App

I simply want to ask what is the recommended way to resolve this with esbuild ?? (which I think is the bees-knees).

I made some progress using --external:react --external:react-dom, as it's clear React and React-dom don't even get bundled in this case. I got stuck with Uncaught Error: Dynamic require of "react-dom/client" is not supported error in the browser, but I think I might be over looking a flag to use Esm6 imports.

But before proceeding down that road, I want to make sure from your perspective, @evanw
that this is the best way to go. (using external rather than bundling React with some sort of de-duplication)

What do you recommend as a method and mechanism to stop multiple-imports of React into applications which import libraries that import React (yet as PeerDependencies) so that esbuild can be used in this situation for bundling React apps that import libs that import React?

My sample project:
cameronelliott/whip-whep-webrtc-react@b872e15

PS
I also noticed others in issue #475 struggling with multiple instances of React:
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)

PPS
You made a comment about how duplicate instances should not impact correctness,
but it absolutely does in the case of React!
#475 (comment)

@cameronelliott
Copy link
Author

It's worth noting how multiple users of Webpack resolved this multiple-instance vs single-instance of React problem:
In Webpack, this config code is how some users resolve the problem:

// In webpack.config.js

  resolve: {
    alias: {
      react: path.resolve('node_modules/react'),
    },
  },

Which apparently causes React to only be output a single time in the bundle, rather than being output twice or more with multiple paths like node_modules/react, and then foolib/node_modules/react ... etc

https://stackoverflow.com/a/42411974/86375

@hyrious
Copy link

hyrious commented Oct 3, 2023

It's not always the bad case when you have multiple instances of React (which is rarely seen but the use case still exists).

Since you mentioned the alias feature in webpack, esbuild also has one. You just have to use --alias:react=react so that all importing to react will be resolved to the one in the root directory of your project.

@cameronelliott
Copy link
Author

@hyrious Thank you, thank you, thank you for taking a minute to point out that option! That seems to be a great way to get past the multiple instances issue. ❤️

@evanw
Copy link
Owner

evanw commented Oct 3, 2023

PS
I also noticed others in issue #475 struggling with multiple instances of React:
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)
#475 (comment)

PPS
You made a comment about how duplicate instances should not impact correctness,
but it absolutely does in the case of React!
#475 (comment)

The issue thread you are linking to is completely irrelevant to your problem. It does not mean what you think it means. That issue is talking about esbuild generating multiple external imports for the same package in the same file, which always results in the JavaScript VM importing a single package. It's impossible for two import statements with the same path in the same JavaScript file to resolve to two different packages. So it's just an aesthetic/efficiency issue and has no effect on correctness.

The fundamental problem here is that you have multiple copies of the react library on the file system, which is presumably something npm did because some of your dependencies have incompatible requirements for which version of react to use. One way to fix that would be to make sure the libraries you are using all have compatible version ranges for react by upgrading/downgrading libraries as appropriate and/or working with the authors of your dependencies to upgrade their use of react.

You can try to hack away the problem using some kind of package alias but that could potentially result in broken code because you are forcing a version of react on a dependency that is outside of its explicitly-specified supported version range. So assuming the version range is there for a legitimate reason, the version of react you are forcing your dependency to use may have a different API and/or behavior than that package expects, which may result in the introduction of subtle bugs.

In any case, this problem is not esbuild's job to fix. The job of a bundling operation is to bundle the files on the file system as they are while respecting their real behavior. Since there are multiple copies of react on the file system, you will get multiple copies of react at run-time when you run that code in a real JavaScript environment like node, so it's correct for esbuild to include multiple copies of react in the bundle (since that's what happens when running that code for real in node).

@evanw
Copy link
Owner

evanw commented Oct 17, 2023

I'm closing this issue because esbuild is working as intended here.

@evanw evanw closed this as not planned Won't fix, can't repro, duplicate, stale Oct 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants