1

I have setup a test project with dynamic module federation using Vite. You can find the test repo here: https://github.com/iconag-bbasmer/vite-module-federation-test

The issue I have though is that if I want to load multiple external modules into the container app it fails as soon as the sub-modules use React Hooks.

I have put the modules that should be loaded into a json file (see container/public/externalRemotes.json). If I only add one component there, it works without problem. But as soon as I add multiple components to load, it breaks because it seems that there is some kind of race condition and React functionality is not available.

In the test project about I have added a simple useState in vapp1/src/components/AppHeader.tsx like this:

const [check, setCheck] = React.useState<boolean>(false);

This simple Hook call causes the app to break. If I remove this, from the component, everything works fine.

The relevant part where I try to dynamically load the components is in container/src/components/RemoteComponent/index.tsx

import React, { FC } from "react";
import ErrorBoundary from "../ErrorBoundary";

type Props = {
  fallback?: string | React.ReactNode;
  modulesToLoad: any[];
  scope?: string;
  [key: string]: any;
};

const loadComponent = (remoteUrl: string, moduleName: string) => async () => {
  const container = await import(remoteUrl);
  const factory = await container.get(moduleName);
  const Module = factory();
  return Module;
};

const RemoteComponent: FC<Props> = ({ modulesToLoad, scope = "default", fallback = null, ...props }) => {
  const [components, setCompoments] = React.useState<any>();

  React.useEffect(() => {
    if (modulesToLoad.length > 0) {
      const promises = modulesToLoad.map(
        async (module: any) => await React.lazy(loadComponent(module.remoteUrl, `./${module.module}`))
      );
      Promise.all(promises).then((results) => {
        let tempComponents: any[] = [];
        results.forEach((Component) => {
          tempComponents.push(
            <ErrorBoundary>
              <React.Suspense fallback={fallback}>{<Component {...props} />}</React.Suspense>
            </ErrorBoundary>
          );
        });
        // debugger;
        setCompoments(tempComponents);
      });
    }
  }, [modulesToLoad]);

  return <>{components && components.map((component: any) => component)}</>;
};

export default RemoteComponent;

And the loaded components get placed into the container app on a button click in container/src/App.tsx

import * as React from "react";
import "./App.css";
import useUserStore from "sbhContainer/UserStore";
import RemoteComponent from "./components/RemoteComponent";

function App() {
  const [user] = useUserStore();
  const [showInfo, setShowInfo] = React.useState<boolean>(false);
  const [loadedModules, setLoadedModules] = React.useState<any[]>([]);

  const loadModules = (e: any) => {
    fetch("./externalRemotes.json").then((response) => {
      response.json().then((obj) => {
        setLoadedModules(obj);
      });
    });
  };

  React.useEffect(() => {
    setShowInfo(true);
  }, [loadedModules]);

  return (
    <div className="App">
      <div>
        <h1>Container</h1>
        User in Container: {user}
      </div>
      <div style={{ marginTop: 10 }}>
        <button onClick={loadModules} style={{ backgroundColor: "#444" }}>
          Load Modules
        </button>
        {showInfo === true ? (
          <>
            <RemoteComponent modulesToLoad={loadedModules} fallback={<div>Loading...</div>} />
          </>
        ) : null}
      </div>
    </div>
  );
}

export default App;

As mentioned above, this is all just test code, so please be patient with me. :-)

Maybe the repo above helps people to understand dynamic module federation but the issue mentioned above unfortunately makes it all unusable. I suspect that the issue is some kind of race condition but I'm not sure how to fix it. So any help would be greatly appreciated.

bbasmer
  • 127
  • 2
  • 11

0 Answers0