๐ป Senior JS/TS Software Engineer
๐ฑ OSS contributor
๐ก Always looking for new software architecture trends
Module federation example using nested routers
Home Page: https://github.com/nebarf/module-federation-react-router-dom
How would you go about using this solution with dynamic remote modules?
I need to configure dynamic remote modules to handle deploying to different environments. Most implementations I've seen pull the remote URLs from either a static manifest or an API and import them using React.lazy
. That doesn't play nicely with the use of the mount
function here.
I've tried something like this but it doesn't seem to work properly. Any suggestions?
Remote app bootstrap.tsx:
import { useRef, useEffect } from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider, useLocation, useNavigate } from 'react-router-dom';
import { createRouter } from './app/routing/router-factory';
import { RoutingStrategy } from './app/routing/types';
const mount = ({
mountPoint,
initialPathname,
routingStrategy,
}: {
mountPoint: HTMLDivElement;
initialPathname?: string;
routingStrategy?: RoutingStrategy;
}) => {
const router = createRouter({ strategy: routingStrategy, initialPathname });
const root = createRoot(mountPoint);
root.render(<RouterProvider router={router} />);
return () => queueMicrotask(() => root.unmount());
};
const RemoteApp1 = ({ initialPathname }: any) => {
const wrapperRef = useRef<HTMLDivElement>(null);
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
const remoteAppNavigationEventHandler = (event: Event) => {
const pathname = (event as CustomEvent<string>).detail;
const newPathname = `${initialPathname}${pathname}`;
if (newPathname === location.pathname) {
return;
}
navigate(newPathname);
};
window.addEventListener(
'[RA1] navigated',
remoteAppNavigationEventHandler
);
return () => {
window.removeEventListener(
'[RA1] navigated',
remoteAppNavigationEventHandler
);
};
}, [location]);
useEffect(() => {
if (location.pathname.startsWith(initialPathname)) {
window.dispatchEvent(
new CustomEvent('[host] navigated', {
detail: location.pathname.replace(initialPathname, ''),
})
);
}
}, [location]);
const isFirstRunRef = useRef(true);
const unmountRef = useRef(() => {});
useEffect(() => {
if (!isFirstRunRef.current) {
return;
}
unmountRef.current = mount({
mountPoint: wrapperRef.current!,
initialPathname: location.pathname.replace(initialPathname, ''),
});
isFirstRunRef.current = false;
}, [location]);
useEffect(() => unmountRef.current, []);
return <div ref={wrapperRef} id="remote-app-1" />;
};
export default RemoteApp1;
Host component RemoteApp1.tsx:
import { loadRemoteModule } from './load-remote-module';
import { lazy, Suspense } from 'react';
import { REMOTE_APP_1_ROUTING_PREFIX } from '../routing/constants';
const remoteApp1Basename= `/${REMOTE_APP_1_ROUTING_PREFIX}`;
const RemoteApp1Module = lazy(() =>
loadRemoteModule('remote-app-1', './Module')
);
const RemoteApp1 = () => {
return (
<Suspense>
<RemoteApp1Module initialPathname={remoteApp1Basename} />
</Suspense>
);
};
export default RemoteApp1 ;
@nebarf I've been testing the new code and ran into an issue and not sure how to solve it.
When changing the children path in app1 to only have a "page-1" and "page-3" (eliminate page 2 completely and update all references from page-2 to page-3 in app1 and shell)
app1>src>routing>routes.tsx
...
- path: "page-2",
+ path: "page-3",
there is a console warning upon navigating to app2/page-2: No routes matched location "/page-2".
Upon navigating back to app1/page-3 there is another console warning: No routes matched location "/page-3".
Navigation works fine in both scenarios.
app1>src>routing>routes.tsx
path: "/",
element: (
<NavigationManager>
<Outlet /> # <== is issue here?
</NavigationManager>
),
I am not that familiar with the NavigationManager for App1 but I'm wondering if this is caused by both routes for app1 and app2 being combined with createMemoryRouter.
So app2 is expecting a child route for app2/page-3 and app1 is expecting a child route for app1/page-2? If so is there a way to separate the routes so that each app only references its own routes?
Hello, I have done setup by referring this repository.
How can navigate from app1 to app2 without reloading like single page routing
When the app is reloaded at http://localhost:8080/app/page-1 or directly enters the URL it occurs on 404. the issue occurs only when the app is hosted or run using npx serve, local development doesn't have any issue
Issue: The project uses react-router-dom v6.3 which removed history as a dependency.
When creating node modules from scratch using package.json file the history module is not installed which throws the above error from the "app1/app2 > src > bootstrap.tsx" & "shell > src > routing > constants.ts" files.
In looking at documentation for react-router-dom upgrade from v5 to v6 it mentions removing history from the package.json as an upgrade step:
"You'll also want to remove the history dependency from your package.json. The history library is a direct dependency of v6 (not a peer dep), so you won't ever import or use it directly. Instead, you'll use the useNavigate() hook for all navigation"
By chance did your node_modules still contain the old reference to the history module enabling the old import from 'history' to work?
This is a nice implementation of the MFE course from Stephen Grider.
Although you could install the history module v5.3 as a hack - since RRD v6 is designed to work with useNavigate() have you found a way to get the history to work with upgraded navigation without reinstalling the history module?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.