// @ts-nocheck // This file is generated by Umi automatically // DO NOT CHANGE IT MANUALLY! // @ts-ignore import type { models as rawModels } from '@@/plugin-model/model'; import isEqual from '/Users/wuhao/Desktop/demos/standaloneframe/frontend/node_modules/fast-deep-equal/index.js'; import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'; type Models = typeof rawModels; type GetNamespaces = { [K in keyof M]: M[K] extends { namespace: string } ? M[K]['namespace'] : never; }[keyof M]; type Namespaces = GetNamespaces; // @ts-ignore const Context = React.createContext<{ dispatcher: Dispatcher }>(null); class Dispatcher { callbacks: Record> = {}; data: Record = {}; update = (namespace: Namespaces) => { if (this.callbacks[namespace]) { this.callbacks[namespace].forEach((cb) => { try { const data = this.data[namespace]; cb(data); } catch (e) { cb(undefined); } }); } }; } interface ExecutorProps { hook: () => any; onUpdate: (val: any) => void; namespace: string; } function Executor(props: ExecutorProps) { const { hook, onUpdate, namespace } = props; const updateRef = useRef(onUpdate); const initialLoad = useRef(false); let data: any; try { data = hook(); } catch (e) { console.error( `plugin-model: Invoking '${namespace || 'unknown'}' model failed:`, e, ); } // 首次执行时立刻返回初始值 useMemo(() => { updateRef.current(data); }, []); // React 16.13 后 update 函数用 useEffect 包裹 useEffect(() => { if (initialLoad.current) { updateRef.current(data); } else { initialLoad.current = true; } }); return null; } const dispatcher = new Dispatcher(); export function Provider(props: { models: Record; children: React.ReactNode; }) { return ( {Object.keys(props.models).map((namespace) => { return ( { dispatcher.data[namespace] = val; dispatcher.update(namespace); }} /> ); })} {props.children} ); } type GetModelByNamespace = { [K in keyof M]: M[K] extends { namespace: string; model: unknown } ? M[K]['namespace'] extends N ? M[K]['model'] extends (...args: any) => any ? ReturnType : never : never : never; }[keyof M]; type Model = GetModelByNamespace; type Selector = (model: Model) => S; type SelectedModel = T extends (...args: any) => any ? ReturnType> : Model; export function useModel(namespace: N): Model; export function useModel( namespace: N, selector: Selector, ): SelectedModel; export function useModel( namespace: N, selector?: Selector, ): SelectedModel { const { dispatcher } = useContext<{ dispatcher: Dispatcher }>(Context); const selectorRef = useRef(selector); selectorRef.current = selector; const [state, setState] = useState(() => selectorRef.current ? selectorRef.current(dispatcher.data[namespace]) : dispatcher.data[namespace], ); const stateRef = useRef(state); stateRef.current = state; const isMount = useRef(false); useEffect(() => { isMount.current = true; return () => { isMount.current = false; }; }, []); useEffect(() => { const handler = (data: any) => { if (!isMount.current) { // 如果 handler 执行过程中,组件被卸载了,则强制更新全局 data // TODO: 需要加个 example 测试 setTimeout(() => { dispatcher.data[namespace] = data; dispatcher.update(namespace); }); } else { const currentState = selectorRef.current ? selectorRef.current(data) : data; const previousState = stateRef.current; if (!isEqual(currentState, previousState)) { // 避免 currentState 拿到的数据是老的,从而导致 isEqual 比对逻辑有问题 stateRef.current = currentState; setState(currentState); } } }; dispatcher.callbacks[namespace] ||= new Set() as any; // rawModels 是 umi 动态生成的文件,导致前面 callback[namespace] 的类型无法推导出来,所以用 as any 来忽略掉 dispatcher.callbacks[namespace].add(handler); dispatcher.update(namespace); return () => { dispatcher.callbacks[namespace].delete(handler); }; }, [namespace]); return state; }