Problem when dynamically registering routes in an application with microfrontends concept

Viewed 575

I have an Typescript + Redux (with RTK) application using the microfrontends concept. All the steps for the construction came from this tutorial: Microfrontends tutorial.

The main component is Microfrontend.tsx (omitted imports):

interface Manifest {
  files: {
    'main.js': string
    'main.js.map': string
    'index.html': string
  }
  entrypoints: string[]
}

const MicroFrontend = ({
  name,
  host,
  module
}: {
  name: string
  host: string | undefined
  module: string
}) => {
  const history = useHistory()

  useEffect(() => {
    const renderMicroFrontend = () => {
      // @ts-ignore
      window[`render${name}`] && window[`render${name}`](`${name}-container`, history)
    }

    if (document.getElementById(name)) {
      renderMicroFrontend()
      return
    }

    const manifestUrl = `${
      isDevProfile ? host : ''
    }/${module}/view/asset-manifest.json`

    fetch(manifestUrl)
      .then(res => res.json())
      .then((manifest: Manifest) => {
        const script = document.createElement('script')
        script.id = name
        script.crossOrigin = ''
        script.src = `${host}${manifest.files['main.js']}`
        script.onload = () => {
          renderMicroFrontend()
        }
        document.head.appendChild(script)
      })

    return () => {
      // @ts-ignore
      window[`unmount${name}`] && window[`unmount${name}`](`${name}-container`)
    }
  })

  return (
    <main id={`${name}-container`} style={{ height: '100%' }} />
  )
}

MicroFrontend.defaultProps = {
  document,
  window
}

export default MicroFrontend

I'm trying to render the routes of the child components in a dynamic way, however, when I do this, I have a very strange effect: Bug.

The code snippet that generates this effect is this (omitted imports):

const App = () => {
  const dispatch = useAppDispatch()
  const { loadWithSuccess } = useSelector(moduleSelectors)
  const avaibleModuleLinks = useSelector(avaibleModuleLinksWhitoutHome)

  useEffect(() => {
    dispatch(fetchAvaibleModules()).then(response =>
      dispatch(fetchAvaibleModuleLinks(response.payload as string[]))
    )
  }, [dispatch])

  return (
    <BrowserRouter>
      <Template>
        <Switch>
          <Route exact={true} path="/" component={Home} />
          {loadWithSuccess ? avaibleModuleLinks?.map(
            (subMenuPath: SubMenuPath | undefined, index: number) => {
              const subMenuPathKey = subMenuPath ? subMenuPath.key : ''
              let micro = () => (
                <MicroFrontend
                  module={subMenuPathKey}
                  host="127.0.0.1"
                  name={subMenuPath ? subMenuPath.key.charAt(0).toUpperCase() : ''}
                />
              )
              return (
                <Route
                  key={index}
                  path={`/dfe/view/${subMenuPathKey}`}
                  component={micro}
                />
              )
            }
          ): <></>}
        </Switch>
      </Template>
    </BrowserRouter>
  )
}

export default App

Only when I don't render routes dynamically do I have the desired effect: desired behavior

The code snippet that generates this effect is this (omitted imports):


const ModuleNfe = () => (
  <MicroFrontend host="127.0.0.1" name="Nfe" module="nfe" />
)

const App = () => {
  const dispatch = useAppDispatch()
  const { loadWithSuccess } = useSelector(moduleSelectors)
  const avaibleModuleLinks = useSelector(avaibleModuleLinksWhitoutHome)

  useEffect(() => {
    dispatch(fetchAvaibleModules()).then(response =>
      dispatch(fetchAvaibleModuleLinks(response.payload as string[]))
    )
  }, [dispatch])

  return (
    <BrowserRouter>
      <Template>
        <Switch>
          <Route exact={true} path="/" component={Home} />
          <Route path="/dfe/view/nfe" component={ModuleNfe} />
        </Switch>
      </Template>
    </BrowserRouter>
  )
}

export default App

As you may have noticed, the desired behavior is for my page to be rendered inside the Template component. But for some reason, this is not the case.

0 Answers
Related