Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/some-rings-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modern-js/plugin-garfish': patch
---

fix: garfish component render error

fix: 修复 garfish 组件渲染失败问题
5 changes: 5 additions & 0 deletions packages/runtime/plugin-garfish/src/cli/code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export const generateCode = async (
) => {
const { mountId } = config.html;
const { enableAsyncEntry } = config.source;
const { disableComponentCompat } =
typeof config.deploy.microFrontend === 'object'
? config.deploy.microFrontend
: {};
const { internalDirectory, internalSrcAlias, metaName, srcDirectory } =
appContext;
await Promise.all(
Expand All @@ -48,6 +52,7 @@ export const generateCode = async (
customBootstrap,
mountId,
appendCode,
disableComponentCompat,
});
const indexFile = path.resolve(
internalDirectory,
Expand Down
7 changes: 6 additions & 1 deletion packages/runtime/plugin-garfish/src/cli/template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const genRenderCode = ({
customEntry,
customBootstrap,
mountId,
disableComponentCompat,
}: {
srcDirectory: string;
internalSrcAlias: string;
Expand All @@ -16,6 +17,7 @@ const genRenderCode = ({
customEntry?: boolean;
customBootstrap?: string | false;
mountId?: string;
disableComponentCompat?: boolean;
}) =>
customEntry
? `import '${formatImportPath(entry.replace(srcDirectory, internalSrcAlias))}'
Expand All @@ -41,7 +43,7 @@ if (!isRenderGarfish()) {
};
}

export const provider = createProvider('${mountId || 'root'}', { customBootstrap });
export const provider = createProvider('${mountId || 'root'}', { customBootstrap, disableComponentCompat: ${disableComponentCompat} });
`;
export const index = ({
srcDirectory,
Expand All @@ -53,6 +55,7 @@ export const index = ({
customBootstrap,
mountId,
appendCode = [],
disableComponentCompat,
}: {
srcDirectory: string;
internalSrcAlias: string;
Expand All @@ -63,6 +66,7 @@ export const index = ({
customBootstrap?: string | false;
mountId?: string;
appendCode?: string[];
disableComponentCompat?: boolean;
}) =>
`import '@${metaName}/runtime/registry/${entryName}';
${genRenderCode({
Expand All @@ -73,6 +77,7 @@ export const index = ({
customEntry,
customBootstrap,
mountId,
disableComponentCompat,
})}
${appendCode.join('\n')}
`;
37 changes: 22 additions & 15 deletions packages/runtime/plugin-garfish/src/runtime/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function createProvider(
{
customBootstrap,
beforeRender,
disableComponentCompat,
}: {
customBootstrap?: (
App: React.ComponentType,
Expand All @@ -24,10 +25,29 @@ export function createProvider(
App: React.ComponentType,
props?: Record<string, any>,
) => Promise<any>;
disableComponentCompat?: boolean;
} = {},
) {
return ({ basename, dom }: { basename: string; dom: HTMLElement }) => {
let root: HTMLElement | Root | null = null;
const SubModuleComponent = disableComponentCompat
? null
: (props: any) => {
const ModernRoot = createRoot(null);
return createPortal(
<ModernRoot basename={basename} {...props} />,
dom.querySelector(`#${id || 'root'}`) || dom,
);
};
const jupiter_submodule_app_key = disableComponentCompat
? null
: (props: any) => {
const ModernRoot = createRoot(null);
return createPortal(
<ModernRoot basename={basename} {...props} />,
dom.querySelector(`#${id || 'root'}`) || dom,
);
};
return {
async render({
basename,
Expand Down Expand Up @@ -70,21 +90,8 @@ export function createProvider(
}
},
// 兼容旧版本
SubModuleComponent: (props: any) => {
const ModernRoot = createRoot(null);
return createPortal(
<ModernRoot basename={basename} {...props} />,
dom.querySelector(`#${id || 'root'}`) || dom,
);
},
jupiter_submodule_app_key: (props: any) => {
const ModernRoot = createRoot(null);

return createPortal(
<ModernRoot basename={basename} {...props} />,
dom.querySelector(`#${id || 'root'}`) || dom,
);
},
SubModuleComponent,
jupiter_submodule_app_key,
};
};
}
27 changes: 26 additions & 1 deletion packages/runtime/plugin-garfish/src/runtime/utils/apps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ function getAppInstance(
manifest?: Manifest,
) {
let locationHref = '';

// Create a callback registry center
// This object is within the closure of getAppInstance and will only be created once for the same sub-app.
// It will be shared by all MicroApp component instances to store the state setter of the currently active component
const componentSetterRegistry = {
current: null as React.Dispatch<
React.SetStateAction<{ component: React.ComponentType<any> | null }>
> | null,
};

function MicroApp(props: MicroProps) {
const appRef = useRef<interfaces.App | null>(null);
const domId = generateSubAppContainerKey(appInfo);
Expand Down Expand Up @@ -137,6 +147,9 @@ or directly pass the "basename":
}, [location]);

useEffect(() => {
// [MODIFIED] Register the current instance's state setter when the component mounts
componentSetterRegistry.current = setSubModuleComponent;

const { setLoadingState, ...userProps } = props;

const loadAppOptions: Omit<interfaces.AppInfo, 'name'> = {
Expand Down Expand Up @@ -164,7 +177,15 @@ or directly pass the "basename":
return {
mount: (...props) => {
if (componetRenderMode && SubComponent) {
setSubModuleComponent({ component: SubComponent });
// [MODIFIED] Get and call the current state setter from the registry center
// This way, even if the mount method is cached, it can still call the setter of the latest component instance
if (componentSetterRegistry.current) {
componentSetterRegistry.current({ component: SubComponent });
} else {
logger(
`[Garfish] MicroApp for "${appInfo.name}" tried to mount, but no active component setter was found.`,
);
}
return undefined;
} else {
logger('MicroApp customer render', props);
Expand Down Expand Up @@ -230,7 +251,11 @@ or directly pass the "basename":
}
}
renderApp();

return () => {
// [MODIFIED] Unregister the state setter when the component unmounts to prevent memory leaks and calls to unmounted components
componentSetterRegistry.current = null;

if (appRef.current) {
const { appInfo } = appRef.current;
if (appInfo.cache) {
Expand Down
2 changes: 2 additions & 0 deletions packages/solutions/app-tools/src/types/config/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export interface MicroFrontend {
*/
externalBasicLibrary?: boolean;
moduleApp?: string;
// provider not generate SubModuleComponent and jupiter_submodule_app_key
disableComponentCompat?: boolean;
}

export interface DeployUserConfig {
Expand Down
Loading