最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin4浏览0评论

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.

发布评论

评论列表(0)

  1. 暂无评论