-
-
Notifications
You must be signed in to change notification settings - Fork 375
Description
Clear and concise description of the problem
I think it would be great to have an option to declare the "bridge" layer exactly via the createLazyComponent call. It would be useful when you need to describe "adapters" for multiple remotes.
e.g. your host uses react-router-dom, but your remote has it's own router and you don't want to install react-router-dom to your remote. And you just want to provide 3 props:
- location
- onRouteChange (listen)
- basename
You can write your host HOC layer specially for that remote, thus your remote is not required to share extra deps (e.g. react-router-dom, react-hook-form, jotai, redux, etc.)
Suggested solution
I see it as withLayer option in createLazyComponent
Host
import { getInstance, loadRemote } from '@module-federation/runtime';
import { useEffect } from 'react'; // host react
import { useLocation, useHistory } from 'react-router-dom' // host react-router dom
import { HostContext } from './context' // host context
const LazyComponent = getInstance()!.createLazyComponent({
loader: () => loadRemote('remote'),
loading: 'loading...',
fallback: ({ error }) => {
if (error instanceof Error && error.message.includes('not exist')) {
return <div>fallback - not existed id</div>;
}
return <div>fallback</div>;
},
// HOC // Allows host to use it's own deps and provide values to remote in convinient form.
// Thus remote doesn't require to share those deps, it just waits for declared props.
// e.g. your host uses react-router-dom, but your remote has it's own router and you don't want to install react-router-dom to your remote
// this logic may be applied to any npm deps or any host variables (e.g. React Context)
withLayer: (App) => {
// i'm not sure how to name it properly
// e.g. bridgeLayer, withLayer, withBridge, withWrapper
return (props) => {
const location = useLocation()
const { listen, createHref } = useHistory()
const { updateUser } = useContext(HostContext)
const basename = createHref(props.prefix)
useEffect(() => {
// ...
}, [])
return <App {...props} location={location} onRouteChange={listen} updateUser={updateUser} basename={basename} />
}
},
});
function HostRedPage() {
// remote App gets props: { prefix, color, location, onRouteChange, updateUser, basename }
return <LazyComponent prefix="/red" color="red" />
}
function HostGreenPage() {
return <LazyComponent prefix="/green" color="green" />
}Remote
import { useEffect } from 'react'; // remote react
export function App({ location, onRouteChange, updateUser, basename ...props }) {
useEffect(() => {
return onRouteChange((location, action) => {
console.log("on host route change");
})
}, [onRouteChange])
return <div>
<div style={{ color: props.color }}>
Provided basename: {basename}
</div>
<button onClick={() => location.push('/home')}>
Open /home page in host
</button>
<button onClick={() => updateUser(null)}>
Logout
</button>
</div>
}
export default createBridgeComponent({
rootComponent: App,
});Alternative
I always can do the same thing without withLayer option
function HostApp() {
const location = useLocation()
return <LazyComponent color="red" location={location} />
}Also
Intead of props we could provide LayerContext from remote to host and share data with that
// remote
const BridgeLayerContext = React.createContext()
export default createBridgeComponent({
rootComponent: App,
bridgeContext: BridgeLayerContext,
});
function App() {
const { basename, location, updateUser } = useContext(BridgeLayerContext)
return '...'
}
// host
import { createContextProvider } from '@module-federation/bridge-react'
const LazyComponent = getInstance()!.createLazyComponent({
// ...
withLayer: (App, BridgeLayerContext) => {
// similar to Context.Provider, but in fact Context.Provider is called inside of remote, not in host
const BridgeLayerProvider = createContextProvider(BridgeLayerContext)
return (props) => {
const location = useLocation()
const { createHref } = useHistory()
const { updateUser } = useContext(HostContext)
const basename = createHref(props.prefix)
useEffect(() => {
// ...
}, [])
return <BridgeLayerProvider location={location} updateUser={updateUser} basename={basename}>
<App {...props} />
</BridgeLayerProvider>
}
},
});I'm not sure if it's possible with suggested implementation, but I think it's possible to implement something similar.
Additional context
I know that @module-federation/bridge-react already supports react-router-dom usage. I used react-router-dom as demonstrative example.
I think current solution is too generic, I want to have much more control over host <-> remote provided props
Validations
- Read the Contributing Guidelines.
- Check that there isn't already an issue that request the same feature to avoid creating a duplicate.