import * as React from "react"; import {ComponentType} from "react"; import {Model, Module, MetaData, GetModule, ModuleGetter} from "./global"; import {invalidview} from "./store"; function isPromiseModule(module: Module | Promise): module is Promise { return typeof module["then"] === "function"; } function isPromiseView(moduleView: ComponentType | Promise>): moduleView is Promise> { return typeof moduleView["then"] === "function"; } function getView>(getModule: GetModule, viewName: N): M["views"][N] | Promise { const result = getModule(); if (isPromiseModule(result)) { return result.then(module => module.views[viewName]); } else { return result.views[viewName]; } } export function loadModel(getModule: GetModule): Promise { const result = getModule(); if (isPromiseModule(result)) { return result.then(module => module.model); } else { return Promise.resolve(result.model); } } interface LoadViewState { Component: ComponentType | null; } interface ViewState { modelReady: boolean; } export type ReturnViews any> = T extends () => Promise> ? R : never; export function loadView, V extends ReturnViews, N extends Extract>(moduleGetter: MG, moduleName: M, viewName: N, loadingComponent: React.ReactNode = null): V[N] { return class Loader extends React.Component { public state: LoadViewState = { Component: null, }; public shouldComponentUpdate(nextProps: any, nextState: LoadViewState) { return nextState.Component !== this.state.Component; } public componentWillMount() { const moduleViewResult = getView(moduleGetter[moduleName], viewName); if (isPromiseView(moduleViewResult)) { moduleViewResult.then(Component => { this.setState({ Component, }); }); } else { this.setState({ Component: moduleViewResult, }); } } public render() { const {Component} = this.state; return Component ? : loadingComponent; } } as any; } export function exportView>(ComponentView: C, model: Model, viewName: string): C { const Comp = ComponentView as any; if (MetaData.isBrowser) { return class Component extends React.PureComponent { public state: ViewState; constructor(props: any, context?: any) { super(props, context); const state = MetaData.clientStore.getState(); const namespace = model.namespace; this.state = { modelReady: !!state[namespace], }; model(MetaData.clientStore).then(() => { if (!this.state.modelReady) { this.setState({modelReady: true}); } }); } public componentWillMount() { const currentViews = MetaData.clientStore.reactCoat.currentViews; if (!currentViews[model.namespace]) { currentViews[model.namespace] = {[viewName]: 1}; } else { const views = currentViews[model.namespace]; if (!views[viewName]) { views[viewName] = 1; } else { views[viewName]++; } } invalidview(); } public componentWillUnmount() { const currentViews = MetaData.clientStore.reactCoat.currentViews; if (currentViews[model.namespace] && currentViews[model.namespace][viewName]) { currentViews[model.namespace][viewName]--; } invalidview(); } public render() { return this.state.modelReady ? : null; } } as any; } else { // ssr时不存在invalidview,也不需要 model初始化,因为必须提前将 model初始化(单向数据流) return Comp; } } /* let autoId: number = 0; export function exportView>(ComponentView: C, model: Model): C { const Comp = ComponentView as any; return class PureComponent extends React.PureComponent { private uid: string = (autoId++).toString(); public static contextTypes = { store: PropTypes.object, }; public render() { return ; } public componentWillMount() { const {store} = this.context; (model as InternalModel)(store, this.uid); } public componentWillUnmount() { const {store} = this.context; (model as InternalModel)(store, this.uid, true); } } as any; } export function exportModel(namespace: string, HandlersClass: {new (): BaseModuleHandlers}): Model { return async (store: ModelStore, viewName?: string, unmount?: boolean) => { const hasInjected = store.reactCoat.injectedModules[namespace]; if (!hasInjected) { store.reactCoat.injectedModules[namespace] = viewName ? {[viewName]: true} : {}; const handlers = new HandlersClass(); (handlers as any).namespace = namespace; (handlers as any).store = store; const actions = injectActions(store, namespace, handlers as any); const hasInited = Boolean(store.getState()[namespace]); if (!hasInited) { const initAction = actions.INIT((handlers as any).initState); await store.dispatch(initAction); } if (viewName) { console.log("mount", namespace, "first Inject", hasInited); return store.dispatch(actions.MOUNT()); } } else { const actions = getModuleActionCreatorList(namespace); if (unmount && viewName) { delete hasInjected[viewName]; if (Object.keys(hasInjected).length === 0) { return store.dispatch(actions.UNMOUNT()); } } else if (viewName) { if (Object.keys(hasInjected).length === 0) { console.log("mount", namespace, "hasInjected"); hasInjected[viewName] = true; return store.dispatch(actions.MOUNT()); } else { hasInjected[viewName] = true; } } } return void 0; }; */