import React , { ReactNode } from 'react' ; import { ComponentType } from 'react-dom/node_modules/@types/react' ; const loadDynamicScript = [ ] ; const useDynamicScript = ( url ) => { const [ ready , setReady ] = React . useState ( false ) ; const [ failed , setFailed ] = React . useState ( false ) ; React . useEffect ( ( ) => { if ( ! url ) { return ; } const element = document . createElement ( "script" ) ; element . src = url ; element . type = "text/javascript" ; element . async = true ; setReady ( false ) ; setFailed ( false ) ; element . onload = ( ) => { loadDynamicScript . push ( url ) ; setReady ( true ) ; } ; element . onerror = ( ) => { setReady ( false ) ; setFailed ( true ) ; } ; document . head . appendChild ( element ) ; return ( ) => { const index = loadDynamicScript . findIndex ( ( ele ) => ele === url ) ; loadDynamicScript . splice ( index , 1 ) ; document . head . removeChild ( element ) ; } ; } , [ url ] ) ; return { ready , failed , } ; } ; type LoadingParam = { url : string ; } ; interface FMReactComponentProps { url : string ; scope : string ; module : string ; moduleDeps ? : { [ dependencyName ] : any ; } ; componentProps ? : { [ name ] : any ; } ; loadingScript ? : ( param : LoadingParam ) => ReactNode ; loadingScriptFailed ? : ( param : LoadingParam ) => ReactNode ; } const shareModuleDeps = async ( scope , moduleDeps ? ) => { if ( ! moduleDeps || moduleDeps . length <= 0 ) return ; let sharedScope = { } ; for ( const dep in moduleDeps ) { const required = moduleDeps [ dep ] ; sharedScope [ dep ] = { [ required . version ] : { get : ( ) => new Promise ( ( resolveShared ) => { resolveShared ( ( ) => required ) ; } ) , loaded : true , } } ; } await window [ scope ] . init ( sharedScope ) ; } ; const react = require ( "react" ) ; const reactDom = require ( "react-dom" ) ; export const FMReactComponent = ( { url , scope , module , moduleDeps = { } , loadingScript = ( ) => < div />, loadingScriptFailed = () => <div / > , componentProps = { } } ) => { const { ready , failed } = useDynamicScript ( url ) ; if ( ready ) { shareModuleDeps ( scope , { "react" : react , "react-dom" : reactDom , ... moduleDeps } ) ; } if ( ! ready ) { return loadingScript ( { url } ) ; } if ( failed ) { return loadingScriptFailed ( { url } ) ; } const Component = React . lazy ( async ( ) => await ( window as any ) [ scope ] . get ( module ) . then ( ( factory ) => { const Module = factory ( ) ; return Module ; } ) ) ; return ( < React . Suspense fallback = "" > < Component { ... componentProps } / > < /React.Suspense>); } ;