fix: exclude shared singleton React from dev optimizeDeps#861
Conversation
commit: |
Tested #861 against the repro — it's necessary but not sufficientThanks for the quick PR @dmchoi77. I tested it directly against the minimal repro (https://github.com/abhayzmp/mf-vite-repro) and unfortunately the duplicate-React error still reproduces. I believe the bug has two independent causes and #861 only addresses the first. How I testedI applied #861's exact source change to the installed plugin in // src/index.ts (built: lib/index.js)
- const shouldBypassOptimizeDep = isLitShare(key) || key === "react" && hasPackageDependency("react-redux", root);
+ const shouldBypassOptimizeDep = isLitShare(key) || key === "react" && shareItem.shareConfig?.singleton === true;Important detail: the host and remote resolve to two separate installed copies of the plugin, because they're on different Vite majors:
I patched both copies (verified the pnpm dev --force # vite --force on both apps → "Forced re-optimization of dependencies"ResultThe remote still fails with the same error: The stack trace shows Why I think there are two causesCause 1 — the Cause 2 — the 1.16.9 share-scope cache-key / singleton-aliasing rewrite (not addressed). After the
This matches the bisection: 1.16.6 is the last working version precisely because it predates this rewrite. The dev Bisection (no
|
@module-federation/vite |
Result |
|---|---|
| 1.16.4 | ✅ Works |
| 1.16.5 | ❌ Invalid hook call |
| 1.16.6 | ✅ Works |
| 1.16.9 | ❌ Cannot read properties of undefined (reading '0') |
| 1.16.10 | ❌ Cannot read properties of null (reading 'useSyncExternalStore') |
| 1.16.10 + #861 | ❌ Still useSyncExternalStore (tested as above) |
Suggestion
#861 looks correct and worth merging on its own (the react-redux gate shouldn't have been there). But a complete fix for #858 also needs the share-scope singleton-aliasing path restored so a remote's loadShare resolves onto the host's React singleton in dev — likely a regression introduced by #828 / #837 / #843.
Happy to re-test any follow-up patch against the same repro.
|
Now that the repo is public and accessible, please feel free to play around, clone and test your changes on it. thanks |
|
I checked this further, and I agree: #861 is necessary but not sufficient, so I’ll keep this PR scoped as “Partially addresses #858”. I’ve prepared a follow-up patch for the second path you identified. It adds canonical shared cache keys with default-scope compatibility aliases ( I’ll open it as a separate PR instead of expanding #861, so this one can stay focused on the optimizeDeps gating fix. Once it’s up, could you re-test it against the same repro? |
|
Thanks @abhayzmp for your check. |

Partially addresses #858
Summary
This prevents shared singleton React from being pre-bundled by Vite's dev
optimizeDepspipeline.Previously,
reactwas excluded from dev dependency optimization only whenreact-reduxwas installed. React-only or React-without-Redux Module Federation setups could still havereactpushed intooptimizeDeps.include, allowing Vite to pre-bundle a remote-local React copy under/node_modules/.vite/deps.The fix changes the bypass condition to use the shared configuration itself: if
reactis configured as a shared singleton, it stays out of dev pre-bundling.Problem
React singleton sharing depends on bare React imports going through the federation shared/loadShare path.
In dev mode, Vite can rewrite optimized dependencies to files such as:
Once that happens, the remote can evaluate against its own optimized React instance while the host's
react-domrenders with the host React dispatcher. That produces duplicate React symptoms such as:The previous guard was too narrow:
That only protected projects with
react-reduxinstalled. Shared singleton React should not depend on whether Redux is present.Changes
shareItem.shareConfig.singleton === true.react-reduxis not installed.Tests
src/__tests__/index.test.tsfor:react-reduxabsent,reactconfigured as a shared singleton,react,react.Verification
Results:
src/__tests__/index.test.tspassed: 56 tests.pnpm exec vitest run --dir srcstill prints the existingMaxListenersExceededWarning, but all tests pass.Compatibility Notes
This change only affects dev-mode optimizeDeps handling for shared singleton
react.Non-singleton React remains on the existing optimizeDeps include path. User-provided
optimizeDeps.includestill takes precedence through the existing include check, preserving the current explicit override behavior.Projects that need React singleton sharing should avoid explicitly adding
reacttooptimizeDeps.include, because that intentionally opts back into Vite dev pre-bundling.