|
/* eslint-disable @typescript-eslint/naming-convention */ |
|
import React, { ComponentType, LazyExoticComponent, useEffect, useState } from 'react'; |
|
/** |
|
* The following is a virtual module generated by vite-plugin-federation. |
|
* It's not available statically, hence we need to declare it's types for TypeScript. |
|
* It contains the methods to set and get remote components. |
|
* The methods are used to load remote components dynamically. |
|
*/ |
|
import { |
|
__federation_method_getRemote as getRemote, |
|
__federation_method_setRemote as setRemote, |
|
__federation_method_wrapDefault as wrapDefault, |
|
type RemoteComponentConfig, |
|
} from 'virtual:__federation__'; |
|
|
|
interface DefaultModule { |
|
default: ComponentType<unknown>; |
|
} |
|
|
|
async function loadRemoteComponent(remote: RemoteComponentConfig): Promise<DefaultModule> { |
|
const { name, componentToImport, ...restConfig } = remote; |
|
setRemote(name, restConfig); |
|
const remoteModule = await getRemote(name, componentToImport); |
|
return wrapDefault(remoteModule, true) as Promise<DefaultModule>; |
|
} |
|
|
|
type LazyComponent = LazyExoticComponent<ComponentType<unknown>>; |
|
|
|
export function useFederatedComponent(remote: RemoteComponentConfig): { |
|
isError: boolean; |
|
Component: LazyComponent | null; |
|
} { |
|
// Key for tracking if the component is already loaded |
|
const key = `${remote.url}-${remote.name}-${remote.from}-${remote.format}-${remote.componentToImport}`; |
|
|
|
const [Component, setComponent] = useState<LazyComponent | null>(null); |
|
const [isError, setIsError] = useState(false); |
|
|
|
/** |
|
* If the key changes, delete the current component. |
|
* The updated component will be loaded in the next useEffect. |
|
*/ |
|
useEffect(() => { |
|
if (Component) setComponent(null); |
|
// eslint-disable-next-line react-hooks/exhaustive-deps |
|
}, [key]); |
|
|
|
useEffect(() => { |
|
async function loadComp(): Promise<void> { |
|
try { |
|
const Comp = React.lazy(async () => loadRemoteComponent(remote)); |
|
setComponent(Comp); |
|
setIsError(false); |
|
} catch (err) { |
|
// eslint-disable-next-line no-console |
|
console.error('>>>>>>>>', err); |
|
setIsError(true); |
|
} |
|
} |
|
if (!Component) { |
|
loadComp().catch(() => {}); |
|
} |
|
// eslint-disable-next-line react-hooks/exhaustive-deps |
|
}, [key, Component]); |
|
|
|
return { isError, Component }; |
|
} |