-
Notifications
You must be signed in to change notification settings - Fork 2k
/
MicroApp.tsx
113 lines (97 loc) · 3.69 KB
/
MicroApp.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { isEqual, noop } from 'lodash';
import {
type SharedProps,
type MicroAppType,
type SharedSlots,
unmountMicroApp,
mountMicroApp,
updateMicroApp,
omitSharedProps,
} from '@qiankunjs/ui-shared';
import React, { type Ref, forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import ErrorBoundary from './ErrorBoundary';
import MicroAppLoader from './MicroAppLoader';
export type Props = SharedProps & SharedSlots<React.ReactNode> & Record<string, unknown>;
function useDeepCompare<T>(value: T): T {
const ref = useRef<T>(value);
if (!isEqual(value, ref.current)) {
ref.current = value;
}
return ref.current;
}
export const MicroApp = forwardRef((componentProps: Props, componentRef: Ref<MicroAppType | undefined>) => {
const { name, autoSetLoading, autoCaptureError, wrapperClassName, className, loader, errorBoundary } = componentProps;
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error>();
const containerRef = useRef<HTMLDivElement>(null);
const microAppRef = useRef<MicroAppType>();
// 未配置自定义 errorBoundary 且开启了 autoCaptureError 场景下,使用插件默认的 errorBoundary,否则使用自定义 errorBoundary
const microAppErrorBoundary = errorBoundary || (autoCaptureError ? (e) => <ErrorBoundary error={e} /> : null);
// 配置了 errorBoundary 才改 error 状态,否则直接往上抛异常
const setComponentError = (e: Error | undefined) => {
if (microAppErrorBoundary) {
setError(e);
// error log 出来,不要吞
if (e) {
console.error(e);
}
} else if (e) {
throw e;
}
};
const onError = (e: Error) => {
setComponentError(e);
setLoading(false);
};
useImperativeHandle(componentRef, () => microAppRef.current);
useEffect(() => {
mountMicroApp({
prevMicroApp: microAppRef.current,
container: containerRef.current!,
componentProps,
setLoading,
setError: setComponentError,
})
.then((app) => {
microAppRef.current = app;
})
.catch((e: Error) => {
onError(e);
});
return () => {
const microApp = microAppRef.current;
if (microApp && microApp.getStatus() === 'MOUNTED') {
// 微应用 unmount 是异步的,中间的流转状态不能确定,所有需要一个标志位来确保 unmount 开始之后不会再触发 update
microApp._unmounting = true;
unmountMicroApp(microApp).catch((e: Error) => {
onError(e);
});
}
};
}, [name]);
useEffect(() => {
updateMicroApp({
name,
microApp: microAppRef.current,
microAppProps: omitSharedProps(componentProps),
setLoading,
});
return noop;
}, [useDeepCompare(omitSharedProps(componentProps))]);
// 未配置自定义 loader 且开启了 autoSetLoading 场景下,使用插件默认的 loader,否则使用自定义 loader
const microAppLoader =
loader || (autoSetLoading ? (loadingStatus) => <MicroAppLoader loading={loadingStatus} /> : null);
const microAppWrapperClassName = wrapperClassName
? `${wrapperClassName} qiankun-micro-app-wrapper`
: 'qiankun-micro-app-wrapper';
const microAppClassName = className ? `${className} qiankun-micro-app-container` : 'qiankun-micro-app-container';
return microAppLoader || microAppErrorBoundary ? (
<div style={{ position: 'relative' }} className={microAppWrapperClassName}>
{microAppLoader && microAppLoader(loading)}
{microAppErrorBoundary && error && microAppErrorBoundary(error)}
<div ref={containerRef} className={microAppClassName} />
</div>
) : (
<div ref={containerRef} className={microAppClassName} />
);
});