@nnnb/prompt
Version:
A factory method that supports various modal components in function call. Support asynchronous collection and function call.
104 lines (84 loc) • 2.79 kB
text/typescript
// 导入深拷贝函数
import cloneDeep from "lodash/cloneDeep";
// 导入 useSyncExternalStore 的导出
import useSyncExternalStoreExports from "use-sync-external-store/shim";
// 获取 useSyncExternalStore
const { useSyncExternalStore } = useSyncExternalStoreExports;
// 定义监听器类型
type Listener = () => void;
// 定义 Store 的基础接口
interface StoreInterface<T = any> {
state: T;
restore: T;
listeners: Set<Listener>;
dispatch: (payload: Partial<T>) => void;
subscribe: (listener: Listener) => () => void;
getSnapshot: () => T;
use: () => T;
}
class Store<T extends object> implements StoreInterface<T> {
public state: T;
public restore: T;
public listeners: Set<Listener>;
constructor(initialState: T) {
this.state = initialState;
this.restore = cloneDeep(initialState);
this.listeners = new Set();
}
private get listenerCount(): number {
return this.listeners.size;
}
public dispatch = (payload: Partial<T>): void => {
this.state = { ...this.state, ...payload };
this.notifyListeners();
};
private notifyListeners(): void {
this.listeners.forEach((listener) => listener());
}
public subscribe = (listener: Listener): () => void => {
this.listeners.add(listener);
return () => {
this.listeners.delete(listener);
if (this.listenerCount === 0) {
this.resetState();
}
};
};
private resetState(): void {
this.state = cloneDeep(this.restore);
}
public getSnapshot = (): T => {
return this.state;
};
public use(): T {
return useStore(this);
}
}
export const create = <T extends object>(initialState: T): T & { use: () => T } => {
const store = new Store<T>(initialState);
const proxy = new Proxy(store, {
get(target, prop: string | symbol, receiver): any {
if (!["subscribe", "getSnapshot", "use"].includes(prop.toString())) {
return target.state[prop as keyof T];
}
return Reflect.get(target, prop, receiver);
},
set(target, prop: string | symbol, value: any): boolean {
if (target.state[prop as keyof T] !== value) {
target.dispatch({ [prop as keyof T]: value } as Partial<T>);
}
return true;
}
});
return proxy as unknown as T & { use: () => T };
};
const useStore = <T>(store: StoreInterface<T>): T => {
const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
// 绑定方法的 this 上下文
Object.keys(state).forEach((key) => {
if (typeof state[key as keyof T] === "function") {
(state[key as keyof T] as Function) = (state[key as keyof T] as Function).bind(store);
}
});
return state;
};