/** * createModuleLoader - 创建模块加载器函数 * 用于 React Navigation 的 getComponent API,实现真正的懒加载 * 组件只在导航到该路由时才被创建和加载 */ import React, { useEffect, useState } from 'react'; import { ModuleRegistry } from './ModuleRegistry'; import { ModuleLoadingPlaceholder } from './ModuleLoadingPlaceholder'; import { ModuleErrorFallback } from './ModuleErrorFallback'; type ModuleState = 'idle' | 'loading' | 'success' | 'error'; export interface ErrorFallbackProps { moduleId: string; error: Error | null; onRetry: () => void; } export interface CreateModuleLoaderOptions { onError?: (error: Error) => void; ErrorFallback?: React.ComponentType; } /** * 创建模块加载器函数 * @param moduleId 模块 ID * @param pickComponent 从模块 exports 中提取组件的函数 * @param options 可选配置 * @returns 返回一个函数,该函数返回 React 组件(用于 getComponent) */ export function createModuleLoader( moduleId: string, pickComponent: (exports: any) => React.ComponentType, options: CreateModuleLoaderOptions = {} ): () => React.ComponentType { return function getModuleComponent() { function ModuleWrappedScreen(props: any) { const [state, setState] = useState('idle'); const [InnerComponent, setInnerComponent] = useState | null>(null); const [error, setError] = useState(null); const [retryCount, setRetryCount] = useState(0); useEffect(() => { let isMounted = true; async function load() { try { setState('loading'); await ModuleRegistry.loadModule(moduleId); if (!isMounted) return; const exports = ModuleRegistry.getModuleExports(moduleId); const Comp = pickComponent(exports); setInnerComponent(() => Comp); setState('success'); } catch (e) { if (!isMounted) return; const err = e instanceof Error ? e : new Error(String(e)); setError(err); setState('error'); options.onError?.(err); } } load(); return () => { isMounted = false; }; }, [moduleId, retryCount]); if (state === 'error') { const FallbackComponent = options.ErrorFallback || ModuleErrorFallback; return ( { setState('idle'); setError(null); setRetryCount((c) => c + 1); }} /> ); } if (state === 'loading' || !InnerComponent) { return ; } return ; } (ModuleWrappedScreen as any).displayName = `ModuleLoader(${moduleId})`; return ModuleWrappedScreen; }; }