Skip to content

Commit a906be7

Browse files
authored
fix: garfish component render error (#7629)
1 parent c6b532c commit a906be7

File tree

6 files changed

+68
-17
lines changed

6 files changed

+68
-17
lines changed

.changeset/some-rings-grow.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@modern-js/plugin-garfish': patch
3+
---
4+
5+
fix: garfish component render error
6+
7+
fix: 修复 garfish 组件渲染失败问题

packages/runtime/plugin-garfish/src/cli/code.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export const generateCode = async (
2323
) => {
2424
const { mountId } = config.html;
2525
const { enableAsyncEntry } = config.source;
26+
const { disableComponentCompat } =
27+
typeof config.deploy.microFrontend === 'object'
28+
? config.deploy.microFrontend
29+
: {};
2630
const { internalDirectory, internalSrcAlias, metaName, srcDirectory } =
2731
appContext;
2832
await Promise.all(
@@ -48,6 +52,7 @@ export const generateCode = async (
4852
customBootstrap,
4953
mountId,
5054
appendCode,
55+
disableComponentCompat,
5156
});
5257
const indexFile = path.resolve(
5358
internalDirectory,

packages/runtime/plugin-garfish/src/cli/template.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const genRenderCode = ({
88
customEntry,
99
customBootstrap,
1010
mountId,
11+
disableComponentCompat,
1112
}: {
1213
srcDirectory: string;
1314
internalSrcAlias: string;
@@ -16,6 +17,7 @@ const genRenderCode = ({
1617
customEntry?: boolean;
1718
customBootstrap?: string | false;
1819
mountId?: string;
20+
disableComponentCompat?: boolean;
1921
}) =>
2022
customEntry
2123
? `import '${formatImportPath(entry.replace(srcDirectory, internalSrcAlias))}'
@@ -41,7 +43,7 @@ if (!isRenderGarfish()) {
4143
};
4244
}
4345
44-
export const provider = createProvider('${mountId || 'root'}', { customBootstrap });
46+
export const provider = createProvider('${mountId || 'root'}', { customBootstrap, disableComponentCompat: ${disableComponentCompat} });
4547
`;
4648
export const index = ({
4749
srcDirectory,
@@ -53,6 +55,7 @@ export const index = ({
5355
customBootstrap,
5456
mountId,
5557
appendCode = [],
58+
disableComponentCompat,
5659
}: {
5760
srcDirectory: string;
5861
internalSrcAlias: string;
@@ -63,6 +66,7 @@ export const index = ({
6366
customBootstrap?: string | false;
6467
mountId?: string;
6568
appendCode?: string[];
69+
disableComponentCompat?: boolean;
6670
}) =>
6771
`import '@${metaName}/runtime/registry/${entryName}';
6872
${genRenderCode({
@@ -73,6 +77,7 @@ export const index = ({
7377
customEntry,
7478
customBootstrap,
7579
mountId,
80+
disableComponentCompat,
7681
})}
7782
${appendCode.join('\n')}
7883
`;

packages/runtime/plugin-garfish/src/runtime/provider.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export function createProvider(
1515
{
1616
customBootstrap,
1717
beforeRender,
18+
disableComponentCompat,
1819
}: {
1920
customBootstrap?: (
2021
App: React.ComponentType,
@@ -24,10 +25,29 @@ export function createProvider(
2425
App: React.ComponentType,
2526
props?: Record<string, any>,
2627
) => Promise<any>;
28+
disableComponentCompat?: boolean;
2729
} = {},
2830
) {
2931
return ({ basename, dom }: { basename: string; dom: HTMLElement }) => {
3032
let root: HTMLElement | Root | null = null;
33+
const SubModuleComponent = disableComponentCompat
34+
? null
35+
: (props: any) => {
36+
const ModernRoot = createRoot(null);
37+
return createPortal(
38+
<ModernRoot basename={basename} {...props} />,
39+
dom.querySelector(`#${id || 'root'}`) || dom,
40+
);
41+
};
42+
const jupiter_submodule_app_key = disableComponentCompat
43+
? null
44+
: (props: any) => {
45+
const ModernRoot = createRoot(null);
46+
return createPortal(
47+
<ModernRoot basename={basename} {...props} />,
48+
dom.querySelector(`#${id || 'root'}`) || dom,
49+
);
50+
};
3151
return {
3252
async render({
3353
basename,
@@ -70,21 +90,8 @@ export function createProvider(
7090
}
7191
},
7292
// 兼容旧版本
73-
SubModuleComponent: (props: any) => {
74-
const ModernRoot = createRoot(null);
75-
return createPortal(
76-
<ModernRoot basename={basename} {...props} />,
77-
dom.querySelector(`#${id || 'root'}`) || dom,
78-
);
79-
},
80-
jupiter_submodule_app_key: (props: any) => {
81-
const ModernRoot = createRoot(null);
82-
83-
return createPortal(
84-
<ModernRoot basename={basename} {...props} />,
85-
dom.querySelector(`#${id || 'root'}`) || dom,
86-
);
87-
},
93+
SubModuleComponent,
94+
jupiter_submodule_app_key,
8895
};
8996
};
9097
}

packages/runtime/plugin-garfish/src/runtime/utils/apps.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ function getAppInstance(
4444
manifest?: Manifest,
4545
) {
4646
let locationHref = '';
47+
48+
// Create a callback registry center
49+
// This object is within the closure of getAppInstance and will only be created once for the same sub-app.
50+
// It will be shared by all MicroApp component instances to store the state setter of the currently active component
51+
const componentSetterRegistry = {
52+
current: null as React.Dispatch<
53+
React.SetStateAction<{ component: React.ComponentType<any> | null }>
54+
> | null,
55+
};
56+
4757
function MicroApp(props: MicroProps) {
4858
const appRef = useRef<interfaces.App | null>(null);
4959
const domId = generateSubAppContainerKey(appInfo);
@@ -137,6 +147,9 @@ or directly pass the "basename":
137147
}, [location]);
138148

139149
useEffect(() => {
150+
// [MODIFIED] Register the current instance's state setter when the component mounts
151+
componentSetterRegistry.current = setSubModuleComponent;
152+
140153
const { setLoadingState, ...userProps } = props;
141154

142155
const loadAppOptions: Omit<interfaces.AppInfo, 'name'> = {
@@ -164,7 +177,15 @@ or directly pass the "basename":
164177
return {
165178
mount: (...props) => {
166179
if (componetRenderMode && SubComponent) {
167-
setSubModuleComponent({ component: SubComponent });
180+
// [MODIFIED] Get and call the current state setter from the registry center
181+
// This way, even if the mount method is cached, it can still call the setter of the latest component instance
182+
if (componentSetterRegistry.current) {
183+
componentSetterRegistry.current({ component: SubComponent });
184+
} else {
185+
logger(
186+
`[Garfish] MicroApp for "${appInfo.name}" tried to mount, but no active component setter was found.`,
187+
);
188+
}
168189
return undefined;
169190
} else {
170191
logger('MicroApp customer render', props);
@@ -230,7 +251,11 @@ or directly pass the "basename":
230251
}
231252
}
232253
renderApp();
254+
233255
return () => {
256+
// [MODIFIED] Unregister the state setter when the component unmounts to prevent memory leaks and calls to unmounted components
257+
componentSetterRegistry.current = null;
258+
234259
if (appRef.current) {
235260
const { appInfo } = appRef.current;
236261
if (appInfo.cache) {

packages/solutions/app-tools/src/types/config/deploy.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export interface MicroFrontend {
1111
*/
1212
externalBasicLibrary?: boolean;
1313
moduleApp?: string;
14+
// provider not generate SubModuleComponent and jupiter_submodule_app_key
15+
disableComponentCompat?: boolean;
1416
}
1517

1618
export interface DeployUserConfig {

0 commit comments

Comments
 (0)