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?
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;
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 ;