Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Remote Modules #7

Open
nvonbenken opened this issue Apr 27, 2023 · 0 comments
Open

Dynamic Remote Modules #7

nvonbenken opened this issue Apr 27, 2023 · 0 comments

Comments

@nvonbenken
Copy link

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 ;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant