Skip to content

Commit e38e48d

Browse files
authored
fix(react-bridge): optimize bridge router pathname (#2696)
1 parent b945d21 commit e38e48d

File tree

3 files changed

+233
-186
lines changed

3 files changed

+233
-186
lines changed

apps/router-demo/router-remote1-2001/rsbuild.config.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ export default defineConfig({
3838
'./export-app': './src/export-App.tsx',
3939
},
4040
shared: {
41-
react: {
42-
singleton: true,
43-
},
44-
'react-dom': {
45-
singleton: true,
46-
},
41+
// react: {
42+
// singleton: true,
43+
// },
44+
// 'react-dom': {
45+
// singleton: true,
46+
// },
4747
// 'react-router-dom': {
4848
// singleton: true,
4949
// },

packages/bridge/bridge-react/src/create.tsx

+62-180
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
2-
import * as ReactRouterDOM from 'react-router-dom';
1+
import React, { forwardRef } from 'react';
32
import type { ProviderParams } from '@module-federation/bridge-shared';
4-
import { LoggerInstance, pathJoin } from './utils';
5-
import { dispatchPopstateEnv } from '@module-federation/bridge-shared';
3+
import { LoggerInstance } from './utils';
64
import {
75
ErrorBoundary,
86
ErrorBoundaryPropsWithComponent,
97
} from 'react-error-boundary';
10-
11-
declare const __APP_VERSION__: string;
8+
import RemoteApp from './remote';
129

1310
export interface RenderFnParams extends ProviderParams {
1411
dom?: any;
@@ -25,79 +22,67 @@ interface RemoteModule {
2522
};
2623
}
2724

28-
interface RemoteAppParams {
29-
name: string;
30-
providerInfo: NonNullable<RemoteModule['provider']>;
31-
dispathPopstate: boolean;
32-
}
33-
34-
const RemoteApp = ({
35-
name,
36-
memoryRoute,
37-
basename,
38-
providerInfo,
39-
dispathPopstate,
40-
...resProps
41-
}: RemoteAppParams & ProviderParams) => {
42-
const rootRef = useRef(null);
43-
const renderDom = useRef(null);
44-
const providerInfoRef = useRef<any>(null);
45-
if (dispathPopstate) {
46-
const location = ReactRouterDOM.useLocation();
47-
const [pathname, setPathname] = useState(location.pathname);
48-
49-
useEffect(() => {
50-
if (pathname !== '' && pathname !== location.pathname) {
51-
LoggerInstance.log(`createRemoteComponent dispatchPopstateEnv >>>`, {
52-
name,
53-
pathname: location.pathname,
54-
});
55-
dispatchPopstateEnv();
56-
}
57-
setPathname(location.pathname);
58-
}, [location]);
59-
}
60-
61-
useEffect(() => {
62-
const renderTimeout = setTimeout(() => {
63-
const providerReturn = providerInfo();
64-
providerInfoRef.current = providerReturn;
65-
const renderProps = {
66-
name,
67-
dom: rootRef.current,
68-
basename,
69-
memoryRoute,
70-
...resProps,
71-
};
72-
renderDom.current = rootRef.current;
25+
function createLazyRemoteComponent<T, E extends keyof T>(info: {
26+
loader: () => Promise<T>;
27+
loading: React.ReactNode;
28+
fallback: ErrorBoundaryPropsWithComponent['FallbackComponent'];
29+
export?: E;
30+
}) {
31+
const exportName = info?.export || 'default';
32+
return React.lazy(async () => {
33+
LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
34+
lazyComponent: info.loader,
35+
exportName,
36+
});
37+
try {
38+
const m = (await info.loader()) as RemoteModule;
39+
// @ts-ignore
40+
const moduleName = m && m[Symbol.for('mf_module_id')];
7341
LoggerInstance.log(
74-
`createRemoteComponent LazyComponent render >>>`,
75-
renderProps,
42+
`createRemoteComponent LazyComponent loadRemote info >>>`,
43+
{ name: moduleName, module: m, exportName },
7644
);
77-
providerReturn.render(renderProps);
78-
});
7945

80-
return () => {
81-
clearTimeout(renderTimeout);
82-
setTimeout(() => {
83-
if (providerInfoRef.current?.destroy) {
84-
LoggerInstance.log(
85-
`createRemoteComponent LazyComponent destroy >>>`,
86-
{ name, basename, dom: renderDom.current },
87-
);
88-
providerInfoRef.current?.destroy({
89-
dom: renderDom.current,
90-
});
91-
}
92-
});
93-
};
94-
}, []);
46+
// @ts-ignore
47+
const exportFn = m[exportName] as any;
9548

96-
//@ts-ignore
97-
return <div ref={rootRef}></div>;
98-
};
49+
if (exportName in m && typeof exportFn === 'function') {
50+
const RemoteAppComponent = forwardRef<
51+
HTMLDivElement,
52+
{
53+
basename?: ProviderParams['basename'];
54+
memoryRoute?: ProviderParams['memoryRoute'];
55+
}
56+
>((props, _ref) => {
57+
return (
58+
<RemoteApp
59+
name={moduleName}
60+
providerInfo={exportFn}
61+
exportName={info.export || 'default'}
62+
{...props}
63+
/>
64+
);
65+
});
9966

100-
(RemoteApp as any)['__APP_VERSION__'] = __APP_VERSION__;
67+
return {
68+
default: RemoteAppComponent,
69+
};
70+
} else {
71+
LoggerInstance.log(
72+
`createRemoteComponent LazyComponent module not found >>>`,
73+
{ name: moduleName, module: m, exportName },
74+
);
75+
throw Error(
76+
`Make sure that ${moduleName} has the correct export when export is ${String(
77+
exportName,
78+
)}`,
79+
);
80+
}
81+
} catch (error) {
82+
throw error;
83+
}
84+
});
85+
}
10186

10287
export function createRemoteComponent<T, E extends keyof T>(info: {
10388
loader: () => Promise<T>;
@@ -114,121 +99,18 @@ export function createRemoteComponent<T, E extends keyof T>(info: {
11499
: {}
115100
: {};
116101

102+
const LazyComponent = createLazyRemoteComponent(info);
103+
117104
return (
118105
props: {
119106
basename?: ProviderParams['basename'];
120107
memoryRoute?: ProviderParams['memoryRoute'];
121108
} & RawComponentType,
122109
) => {
123-
const exportName = info?.export || 'default';
124-
let basename = '/';
125-
let enableDispathPopstate = false;
126-
let routerContextVal: any;
127-
try {
128-
ReactRouterDOM.useLocation();
129-
enableDispathPopstate = true;
130-
} catch {
131-
enableDispathPopstate = false;
132-
}
133-
134-
if (props.basename) {
135-
basename = props.basename;
136-
} else if (enableDispathPopstate) {
137-
const ReactRouterDOMAny: any = ReactRouterDOM;
138-
// Avoid building tools checking references
139-
const useRouteMatch = ReactRouterDOMAny['use' + 'RouteMatch']; //v5
140-
const useHistory = ReactRouterDOMAny['use' + 'History']; //v5
141-
const useHref = ReactRouterDOMAny['use' + 'Href'];
142-
const UNSAFE_RouteContext = ReactRouterDOMAny['UNSAFE_' + 'RouteContext'];
143-
144-
if (UNSAFE_RouteContext /* react-router@6 */) {
145-
if (useHref) {
146-
basename = useHref?.('/');
147-
}
148-
routerContextVal = useContext(UNSAFE_RouteContext);
149-
if (
150-
routerContextVal &&
151-
routerContextVal.matches &&
152-
routerContextVal.matches[0] &&
153-
routerContextVal.matches[0].pathnameBase
154-
) {
155-
basename = pathJoin(
156-
basename,
157-
routerContextVal.matches[0].pathnameBase || '/',
158-
);
159-
}
160-
} /* react-router@5 */ else {
161-
const match = useRouteMatch?.(); // v5
162-
if (useHistory /* react-router@5 */) {
163-
// there is no dynamic switching of the router version in the project
164-
// so hooks can be used in conditional judgment
165-
const history = useHistory?.();
166-
// To be compatible to [email protected] and @5.3.0 we cannot write like this `history.createHref(pathname)`
167-
basename = history?.createHref?.({ pathname: '/' });
168-
}
169-
if (match /* react-router@5 */) {
170-
basename = pathJoin(basename, match?.path || '/');
171-
}
172-
}
173-
}
174-
175-
const LazyComponent = useMemo(() => {
176-
//@ts-ignore
177-
return React.lazy(async () => {
178-
LoggerInstance.log(`createRemoteComponent LazyComponent create >>>`, {
179-
basename,
180-
lazyComponent: info.loader,
181-
exportName,
182-
props,
183-
routerContextVal,
184-
});
185-
try {
186-
const m = (await info.loader()) as RemoteModule;
187-
// @ts-ignore
188-
const moduleName = m && m[Symbol.for('mf_module_id')];
189-
LoggerInstance.log(
190-
`createRemoteComponent LazyComponent loadRemote info >>>`,
191-
{ basename, name: moduleName, module: m, exportName, props },
192-
);
193-
194-
// @ts-ignore
195-
const exportFn = m[exportName] as any;
196-
197-
if (exportName in m && typeof exportFn === 'function') {
198-
return {
199-
default: () => (
200-
<RemoteApp
201-
name={moduleName}
202-
dispathPopstate={enableDispathPopstate}
203-
{...info}
204-
{...props}
205-
providerInfo={exportFn}
206-
basename={basename}
207-
/>
208-
),
209-
};
210-
} else {
211-
LoggerInstance.log(
212-
`createRemoteComponent LazyComponent module not found >>>`,
213-
{ basename, name: moduleName, module: m, exportName, props },
214-
);
215-
throw Error(
216-
`Make sure that ${moduleName} has the correct export when export is ${String(
217-
exportName,
218-
)}`,
219-
);
220-
}
221-
} catch (error) {
222-
throw error;
223-
}
224-
});
225-
}, [exportName, basename, props.memoryRoute]);
226-
227-
//@ts-ignore
228110
return (
229111
<ErrorBoundary FallbackComponent={info.fallback}>
230112
<React.Suspense fallback={info.loading}>
231-
<LazyComponent />
113+
<LazyComponent {...props} />
232114
</React.Suspense>
233115
</ErrorBoundary>
234116
);

0 commit comments

Comments
 (0)