Description
flattenModule in federation_fn_import.js uses Object.assign({}, module.default, module) to merge a shared module's default export with its named exports. This creates a shallow snapshot at load time.
React 19 stores its hooks dispatcher in a mutable property:
React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE.H
This property is null at module load time and only set during render. The Object.assign snapshot captures null permanently, so any component using React hooks via the shared module gets:
TypeError: Cannot read properties of null (reading 'useMemoCache')
or similar errors for any hook (useState, useEffect, etc.) when running with React Compiler (babel-plugin-react-compiler).
Reproduction
- Host app shares
react and react-dom
- Remote app uses
babel-plugin-react-compiler (which emits useMemoCache calls)
- Remote loads in host → crash because hooks dispatcher is
null in the snapshot
Expected behavior
Shared modules should preserve live bindings to mutable internal state.
Suggested fix
Replace Object.assign with a Proxy that delegates property access to the original module object at access time (not load time). This preserves live mutable state.
// Before (snapshot — breaks live state)
module = Object.assign({}, module.default, module)
// After (proxy — preserves live bindings)
const originalModule = module
module = new Proxy(module.default, {
get(target, prop) {
if (prop !== 'default' && prop in originalModule) return originalModule[prop]
return target[prop]
},
has(target, prop) {
return prop in originalModule || prop in target
},
ownKeys(target) {
const keys = new Set([...Reflect.ownKeys(target), ...Reflect.ownKeys(originalModule)])
keys.delete('default')
return [...keys]
}
})
Note: The originalModule reference is critical — without it, prop in module after reassignment triggers the Proxy's own has trap recursively (stack overflow).
Environment
- vite-plugin-federation: 1.4.1
- React: 19.x
- babel-plugin-react-compiler: latest
- Vite: 7.x
Description
flattenModuleinfederation_fn_import.jsusesObject.assign({}, module.default, module)to merge a shared module's default export with its named exports. This creates a shallow snapshot at load time.React 19 stores its hooks dispatcher in a mutable property:
This property is
nullat module load time and only set during render. TheObject.assignsnapshot capturesnullpermanently, so any component using React hooks via the shared module gets:or similar errors for any hook (
useState,useEffect, etc.) when running with React Compiler (babel-plugin-react-compiler).Reproduction
reactandreact-dombabel-plugin-react-compiler(which emitsuseMemoCachecalls)nullin the snapshotExpected behavior
Shared modules should preserve live bindings to mutable internal state.
Suggested fix
Replace
Object.assignwith aProxythat delegates property access to the original module object at access time (not load time). This preserves live mutable state.Environment