1- import React , { useContext , useEffect , useMemo , useRef , useState } from 'react' ;
2- import * as ReactRouterDOM from 'react-router-dom' ;
1+ import React , { forwardRef } from 'react' ;
32import 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' ;
64import {
75 ErrorBoundary ,
86 ErrorBoundaryPropsWithComponent ,
97} from 'react-error-boundary' ;
10-
11- declare const __APP_VERSION__ : string ;
8+ import RemoteApp from './remote' ;
129
1310export 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
10287export 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