Today's fast-paced tech realm presents challenges with different React versions coexisting in one environment. This article explores integrating React 17 and React 18 within Webpack Module Federation, ensuring a seamless partnership.
The interaction between React 17 (father app) and React 18 (child app) faces inherent challenges due to their distinct virtualized DOMs. Rendering both together can spark version conflicts, demanding a thoughtful strategy for integration.
Setting Up the Father App (React 17)
In the father app, we face the challenge of incorporating a child app built with React 18 while maintaining isolation. To address this, we create a designated <div>
within the father app where the React 18 child will render, avoiding conflicts with the React 17 DOM structure.
// Father App (React 17)
const componentCleanup = useCallback(() => {
unmount()
}, [remotes.name])
useEffect(() => {
mount({
idParent: "children_app"
})
window.addEventListener('beforeunload', componentCleanup)
return () => {
componentCleanup()
window.removeEventListener('beforeunload', componentCleanup)
}
}, [componentCleanup])
<div id="children_app" />
Child App (React 18) Integration
The child app, developed with React 18, employs a mount controller to handle the rendering process within the specified <div>
of the father app. This ensures the creation of an isolated instance of React 18, mitigating version conflicts.
import { createRoot } from 'react-dom/client'
import App from './App/appMain/AppMain'
window.childapp_ready = false
const RootCache = new Map()
function handleRecoverableError(error) {
console.error('CHILD-APP ERROR:', error)
}
function mount({ idParent }) {
if (window.childapp_ready) return
const container = document.getElementById(idParent)
const root = createRoot(container, {
onRecoverableError: handleRecoverableError
})
root.render(<App />)
window.childapp_ready = true
RootCache.set(idParent, root)
return root
}
function unmount({ idParent }) {
RootCache.get(idParent)?.unmount()
window.childapp_ready = false
}
export default { mount, unmount }
Webpack Configuration for Child App
The webpack configuration for the child app ensures proper sharing and handling of React versions within the Webpack Module Federation setup:
module.exports = {
name: 'childapp',
path: 'build-child',
port: 3333,
openBrowser: false,
remotes: [],
exposes: [
{
componentName: 'App',
componentPath: './src/App/appMain/AppMain.tsx'
},
{
componentName: 'Mount',
componentPath: './src/mountApp.js'
}
],
shared: {
react: {
requiredVersion: '*',
import: 'react',
shareKey: 'react',
shareScope: 'default',
singleton: false
},
'react-dom': {
requiredVersion: dependencies['react-dom'],
singleton: true
}
}
}
Managing React 18 Isolation
By leveraging this structured approach, invoking the mount
function within the father app ensures an encapsulated instance of React 18 is rendered within the designated <div>
. This segregation prevents interference between the React 17 and React 18 instances, promoting a conflict-free environment.
Now we can see that we have two reacts working in the same page!
Closing Thoughts
Managing the integration of disparate React versions demands meticulous planning and implementation. Utilizing Webpack Module Federation provides a strategic avenue to ensure the coexistence of React 17 and React 18 without version conflicts.
example github project: https://github.com/module-federation/module-federation-examples/tree/master/different-react-versions-isolated
Feel free to connect with me on LinkedIn for further discussions and insights into React version integration using Webpack Module Federation.
Top comments (0)