javascript - Webpack module federation and react-router-dom - Stack Overflow

How to properly setup ModuleFederation and react-router-dom so that I can haveRouter and routes defined

How to properly setup ModuleFederation and react-router-dom so that I can have

  • Router and routes defined in Host app
  • and remote Header app has <Link> ponents pointing to the routes defined in Host?

However, the setup below fails to the following error:

index.js:15 Uncaught Error: useHref() may be used only in the context of a <Router> ponent.

The setup:

Host mfe app, localhost:3001

...
import { BrowserRouter } from 'react-router-dom'

const Header = lazy(() => import("header/Header"))

const Host = () => {
  return (
    <BrowserRouter>
       <React.Suspense fallback="Loading Header...">
         <Header />
       </React.Suspense>
       <Switch>
         <Route path="/input">
           <InputFormView />
         </Route>
         <Route path="/">
            <ListView />
         </Route>
       </Switch>       
    </BrowserRouter>)
}

...

Host's webpack.config.js


...

plugins: [
  new ModuleFederationPlugin({
      name: 'host',
      remotes: {        
        header: 'header@http://localhost:3002/remoteEntry.js'
      },
      exposes: {
      },
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        "react-dom": {
          singleton: true,
          requiredVersion: deps["react-dom"],
        },       
        "react-router-dom": {
          singleton: true,
          requiredVersion: deps["react-router-dom"],
        }
      },
    }),
...

Header mfe app, localhost:3002

...

import { Link } from 'react-router-dom'

const Header = () => {
 return (
   <div id="header">
     <h1> Header </h1>
     <Link to="/input"> 
        <button type="button"> Input form </button>        
     </Link>
   </div> 
 )

...

Header's webpack.config.js


...

 new ModuleFederationPlugin({
      name: 'header',
      filename: 'remoteEntry.js',
      exposes: {
        './Header': './src/Components/Header'
      },
      remotes: {},      
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        "react-dom": {
          singleton: true,
          requiredVersion: deps["react-dom"],
        },      
        "react-router-dom": {
          singleton: true,
          requiredVersion: deps["react-router-dom"],
        }
      },
    }),
...

But, if I wrap also Header in BrowserRouter I encounter the following error instead:

index.js:15 Uncaught Error: You cannot render a <Router> inside another <Router>. You should never have more than one in your app.

How to properly setup ModuleFederation and react-router-dom so that I can have

  • Router and routes defined in Host app
  • and remote Header app has <Link> ponents pointing to the routes defined in Host?

However, the setup below fails to the following error:

index.js:15 Uncaught Error: useHref() may be used only in the context of a <Router> ponent.

The setup:

Host mfe app, localhost:3001

...
import { BrowserRouter } from 'react-router-dom'

const Header = lazy(() => import("header/Header"))

const Host = () => {
  return (
    <BrowserRouter>
       <React.Suspense fallback="Loading Header...">
         <Header />
       </React.Suspense>
       <Switch>
         <Route path="/input">
           <InputFormView />
         </Route>
         <Route path="/">
            <ListView />
         </Route>
       </Switch>       
    </BrowserRouter>)
}

...

Host's webpack.config.js


...

plugins: [
  new ModuleFederationPlugin({
      name: 'host',
      remotes: {        
        header: 'header@http://localhost:3002/remoteEntry.js'
      },
      exposes: {
      },
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        "react-dom": {
          singleton: true,
          requiredVersion: deps["react-dom"],
        },       
        "react-router-dom": {
          singleton: true,
          requiredVersion: deps["react-router-dom"],
        }
      },
    }),
...

Header mfe app, localhost:3002

...

import { Link } from 'react-router-dom'

const Header = () => {
 return (
   <div id="header">
     <h1> Header </h1>
     <Link to="/input"> 
        <button type="button"> Input form </button>        
     </Link>
   </div> 
 )

...

Header's webpack.config.js


...

 new ModuleFederationPlugin({
      name: 'header',
      filename: 'remoteEntry.js',
      exposes: {
        './Header': './src/Components/Header'
      },
      remotes: {},      
      shared: {
        ...deps,
        react: {
          singleton: true,
          requiredVersion: deps.react,
        },
        "react-dom": {
          singleton: true,
          requiredVersion: deps["react-dom"],
        },      
        "react-router-dom": {
          singleton: true,
          requiredVersion: deps["react-router-dom"],
        }
      },
    }),
...

But, if I wrap also Header in BrowserRouter I encounter the following error instead:

index.js:15 Uncaught Error: You cannot render a <Router> inside another <Router>. You should never have more than one in your app.

Share Improve this question asked Jan 9, 2022 at 8:04 anmatikaanmatika 1,7116 gold badges23 silver badges30 bronze badges 1
  • Was your issue solved? – Arjun Mudhaliyar Commented Nov 15, 2022 at 13:42
Add a ment  | 

1 Answer 1

Reset to default 3

In your remote app you should wrap your header ponent inside a BrowserRouter, but the ponent that contains the BrowserRouter should not be exposed. In the example I am using react-router-dom v6.

Use another ponent (in my example it's Test.js, which will be used inside index.js, but this won't be exposed by the module federation and it is used just for local development of the remote app). Header is the ponent that you want to expose to module federation and use in the other app (as you have it already in your webpack config)

import React from 'react';
import {
    BrowserRouter,
    Routes,
    Route,
  } from "react-router-dom";
import Header from './Header';

const localRouter = () => {
  return (
    <BrowserRouter>
        <Routes>
            <Route path="/" element={<div>home<Header></Header></div>}></Route>
            <Route path="input"element={<div>input</div>}/>
        </Routes>
    </BrowserRouter>)
}

export default localRouter;

Then in your host app you can use the Header ponent in a similar way as you would in the remote app.

import React from 'react';
import {
  BrowserRouter,
  Routes,
  Route,
  Link
} from "react-router-dom";

const Header = React.lazy(() => import('header/Header'));

const HostApp = () => (
  <>
    <div>Hello, I'm the host app!</div>
    <BrowserRouter>
    <Routes>
        <Route path="/" element={<div>home
                    <React.Suspense fallback="loading...">
                        <Header />
                    </React.Suspense>
              </div>}></Route>
        <Route path="input"element={<div>input</div>}/>

... some other routes

    </Routes>
  </BrowserRouter>
  </>
);

export default HostApp;

The idea is the remote app needs the header to run inside a BrowserRouter for local development, but when used in a host application the Header ponent will use the BrowserRouter from the host application, since the BrowserRouter from the remote app is not exposed anywhere by the module federation.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744263000a4565727.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信