Creo que este es un problema de React duplicado
Comportamiento actual: en el proyecto container , funciona bien cuando enruto a sub-app/foo . Pero cuando me dirijo a la sub-app , aparece el siguiente error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: You might have mismatching versions of React and the renderer (such as React DOM) You might be breaking the Rules of Hooks You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem. Aviso: sub-app/foo obtendrá un componente de clase. sub-app obtendrá un componente de ganchos React
Comportamiento esperado : el resultado que espero es que el código de la subApp funcione bien en el container
La estructura del proyecto:
/mono-repo packages - container - subApp // /packages/container/src/App.js import React from 'react' import { BrowserRouter, Switch, Route, Link } from 'react-router-dom'; import About from './About'; import Header from './Header'; import Loadable from 'react-loadable' const Logo = () => { return <div>LOGO</div> } const loadableComponent = (loader) => { return Loadable({ loader: () => loader().then( (res) => { return res }, (e) => () => { console.log(e) } ), loading() { return ( <div>loading</div> ) } }) } const loadSubApp = (subAppInfo) => { const { name, host } = subAppInfo return new Promise((resolve, reject)=> { fetch(`${host}/${name}/asset-manifest.json`) .then(res => res.json()) .then(manifest => { const script = document.createElement('script'); script.src = `${host}${manifest.files['main.js']}`; const timeout = setTimeout(()=>{ console.error(`MicroApp ${name} timeout`); reject(new Error(`MicroApp ${name} timeout`)); },20000) script.onload = () => { clearTimeout(timeout) const app = window[name] console.log({app, name}) console.log(`MicroApp ${name} loaded success`); resolve(app) } script.onerror = (e) => { clearTimeout(timeout); console.error(`MicroApp ${name} loaded error`, e); reject(e) } document.body.appendChild(script); }) }) } const subLoader = (name) => async () => { const App = await loadSubApp({ name: 'subApp', host: process.env.REACT_APP_SUBAPP_HOST }) console.log({App, window}) return App[name] } const App = () => { return ( <BrowserRouter> <React.Fragment> <Logo /> <ul> <li> <Link to="/">Home</Link> </li> <li> <Link to="/header">header</Link> </li> <li> <Link to="/sub-app">subApp</Link> </li> <li> <Link to="/sub-app/foo">subApp foo</Link> </li> </ul> <Switch> <Route exact path="/" component={About} /> <Route exact path="/header" render={() => <Header /> }/> <Route exact path="/sub-app/foo" render={()=> { const RenderSubApp = loadableComponent(subLoader('Foo')) return <RenderSubApp /> }}/> <Route exact path="/sub-app" render={()=> { const RenderSubApp = loadableComponent(subLoader('Count')) return <RenderSubApp /> }}/> </Switch> </React.Fragment> </BrowserRouter> ) } // /packages/subApp/config-overrides.js const { override, // addWebpackAlias } = require("customize-cra"); // const path = require("path"); const dropConsole = () => { return (config) => { if (config.optimization.minimizer) { config.optimization.minimizer.forEach((minimizer) => { if (minimizer.constructor.name === "TerserPlugin") { minimizer.options.terserOptions.compress.drop_console = true; } }); } return config; }; }; const optBuild= () => config => { config.optimization.runtimeChunk = false; config.optimization.splitChunks = { cacheGroups: { default: false, }, }; return config } const disableSourceMap = () => (config) => { if (process.env.NODE_ENV === "production") { config.devtool = false; } return config; }; const customizeCraOverride = override( disableSourceMap(), dropConsole(), optBuild(), // addWebpackAlias({ // 'react': path.resolve(__dirname, '../container/node_modules/react'), // 'react-dom': path.resolve(__dirname, '../container/node_modules/react-dom') // }) ); const webpack = (config, env) => { const webpackConfig = customizeCraOverride(config, env); return { ...webpackConfig, output: { ...webpackConfig.output, library: "subApp", libraryTarget: 'window', }, // externals: { // 'react': 'react', // 'react-dom': 'react-dom' // }, }; }; module.exports = { webpack, };El enlace del proyecto bug-demo
esas soluciones no me funcionan
No estoy seguro de si esto resuelve completamente su problema, pero el error desaparece y las rutas funcionan correctamente. No se carga nada, estoy bastante seguro de que no tengo las variables de entorno correctas. Pero, básicamente, solo tenía un código de enrutador defectuoso en su archivo /packages/container/src/App.js . Cloné el repositorio y esto es a lo que lo cambié:
<Switch> <Route exact path="/" component={About} /> <Route exact path="/header" render={() => <Header /> }/> <Route exact path="/sub-app" render={()=> { const RenderSubApp = loadableComponent(subLoader('Count')) return <RenderSubApp /> }}> <Route exact path="/foo" render={()=> { const RenderSubApp = loadableComponent(subLoader('Foo')) return <RenderSubApp /> }}/> </Route> </Switch> Observe cómo la ruta /foo se convierte en un elemento secundario de la ruta /sub-app . Aunque el error desapareció, React aún muestra un mensaje de advertencia: Warning: You should not use <Route render> and <Route children> in the same route; <Route render> will be ignored . No estoy exactamente seguro de cómo funciona lerna, pero podría haber alguna solución para esto.
Consulte esta sección de los documentos de react-router.
Finalmente resuelto este problema.
// /packages/subApp/config-overrides.js ... const webpack = (config, env) => { const webpackConfig = customizeCraOverride(config, env); return { ...webpackConfig, output: { ...webpackConfig.output, library: "subApp", libraryTarget: 'window', }, externals: { 'react': 'React', 'react-dom': 'ReactDOM' }, }; }; module.exports = { webpack }; // packages/container/src/index.js ... export { React, ReactDOM } // packages/container/config-overrides.js ... const webpack = (config, env) => { const webpackConfig = customizeCraOverride(config, env); return { ...webpackConfig, output: { ...webpackConfig.output, libraryTarget: 'umd', }, } }; module.exports = { webpack };