You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // @ts-nocheck
  2. // This file is generated by Umi automatically
  3. // DO NOT CHANGE IT MANUALLY!
  4. // @ts-ignore
  5. import type { models as rawModels } from '@@/plugin-model/model';
  6. import isEqual from 'E:/project/myproject/rencaishichanghoutai/node_modules/fast-deep-equal/index.js';
  7. import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
  8. type Models = typeof rawModels;
  9. type GetNamespaces<M> = {
  10. [K in keyof M]: M[K] extends { namespace: string }
  11. ? M[K]['namespace']
  12. : never;
  13. }[keyof M];
  14. type Namespaces = GetNamespaces<Models>;
  15. // @ts-ignore
  16. const Context = React.createContext<{ dispatcher: Dispatcher }>(null);
  17. class Dispatcher {
  18. callbacks: Record<Namespaces, Set<Function>> = {};
  19. data: Record<Namespaces, unknown> = {};
  20. update = (namespace: Namespaces) => {
  21. if (this.callbacks[namespace]) {
  22. this.callbacks[namespace].forEach((cb) => {
  23. try {
  24. const data = this.data[namespace];
  25. cb(data);
  26. } catch (e) {
  27. cb(undefined);
  28. }
  29. });
  30. }
  31. };
  32. }
  33. interface ExecutorProps {
  34. hook: () => any;
  35. onUpdate: (val: any) => void;
  36. namespace: string;
  37. }
  38. function Executor(props: ExecutorProps) {
  39. const { hook, onUpdate, namespace } = props;
  40. const updateRef = useRef(onUpdate);
  41. const initialLoad = useRef(false);
  42. let data: any;
  43. try {
  44. data = hook();
  45. } catch (e) {
  46. console.error(
  47. `plugin-model: Invoking '${namespace || 'unknown'}' model failed:`,
  48. e,
  49. );
  50. }
  51. // 首次执行时立刻返回初始值
  52. useMemo(() => {
  53. updateRef.current(data);
  54. }, []);
  55. // React 16.13 后 update 函数用 useEffect 包裹
  56. useEffect(() => {
  57. if (initialLoad.current) {
  58. updateRef.current(data);
  59. } else {
  60. initialLoad.current = true;
  61. }
  62. });
  63. return null;
  64. }
  65. const dispatcher = new Dispatcher();
  66. export function Provider(props: {
  67. models: Record<string, any>;
  68. children: React.ReactNode;
  69. }) {
  70. return (
  71. <Context.Provider value={{ dispatcher }}>
  72. {Object.keys(props.models).map((namespace) => {
  73. return (
  74. <Executor
  75. key={namespace}
  76. hook={props.models[namespace]}
  77. namespace={namespace}
  78. onUpdate={(val) => {
  79. dispatcher.data[namespace] = val;
  80. dispatcher.update(namespace);
  81. }}
  82. />
  83. );
  84. })}
  85. {props.children}
  86. </Context.Provider>
  87. );
  88. }
  89. type GetModelByNamespace<M, N> = {
  90. [K in keyof M]: M[K] extends { namespace: string; model: unknown }
  91. ? M[K]['namespace'] extends N
  92. ? M[K]['model'] extends (...args: any) => any
  93. ? ReturnType<M[K]['model']>
  94. : never
  95. : never
  96. : never;
  97. }[keyof M];
  98. type Model<N> = GetModelByNamespace<Models, N>;
  99. type Selector<N, S> = (model: Model<N>) => S;
  100. type SelectedModel<N, T> = T extends (...args: any) => any
  101. ? ReturnType<NonNullable<T>>
  102. : Model<N>;
  103. export function useModel<N extends Namespaces>(namespace: N): Model<N>;
  104. export function useModel<N extends Namespaces, S>(
  105. namespace: N,
  106. selector: Selector<N, S>,
  107. ): SelectedModel<N, typeof selector>;
  108. export function useModel<N extends Namespaces, S>(
  109. namespace: N,
  110. selector?: Selector<N, S>,
  111. ): SelectedModel<N, typeof selector> {
  112. const { dispatcher } = useContext<{ dispatcher: Dispatcher }>(Context);
  113. const selectorRef = useRef(selector);
  114. selectorRef.current = selector;
  115. const [state, setState] = useState(() =>
  116. selectorRef.current
  117. ? selectorRef.current(dispatcher.data[namespace])
  118. : dispatcher.data[namespace],
  119. );
  120. const stateRef = useRef<any>(state);
  121. stateRef.current = state;
  122. const isMount = useRef(false);
  123. useEffect(() => {
  124. isMount.current = true;
  125. return () => {
  126. isMount.current = false;
  127. };
  128. }, []);
  129. useEffect(() => {
  130. const handler = (data: any) => {
  131. if (!isMount.current) {
  132. // 如果 handler 执行过程中,组件被卸载了,则强制更新全局 data
  133. // TODO: 需要加个 example 测试
  134. setTimeout(() => {
  135. dispatcher.data[namespace] = data;
  136. dispatcher.update(namespace);
  137. });
  138. } else {
  139. const currentState = selectorRef.current
  140. ? selectorRef.current(data)
  141. : data;
  142. const previousState = stateRef.current;
  143. if (!isEqual(currentState, previousState)) {
  144. // 避免 currentState 拿到的数据是老的,从而导致 isEqual 比对逻辑有问题
  145. stateRef.current = currentState;
  146. setState(currentState);
  147. }
  148. }
  149. };
  150. dispatcher.callbacks[namespace] ||= new Set() as any; // rawModels 是 umi 动态生成的文件,导致前面 callback[namespace] 的类型无法推导出来,所以用 as any 来忽略掉
  151. dispatcher.callbacks[namespace].add(handler);
  152. dispatcher.update(namespace);
  153. return () => {
  154. dispatcher.callbacks[namespace].delete(handler);
  155. };
  156. }, [namespace]);
  157. return state;
  158. }