在 TypeScript 中回答,但它可以很容易地转换为 vanilla JS。为初学者准备这些基本类型
interface HasLastModified {
lastModifiedAt?: number;
}
interface StoreState{
count: number;
}
interface StoreActions {
increaseCounter: (by: number) => void;
}
type Store = StoreState & StoreActions;
你现在至少有两个选择:
选项 1:将 lastModifiedAt 属性作为商店状态的一部分
基于 zustand 文档中的 this article。这是您在问题中描述的情况。
import { create, StateCreator, StoreMutatorIdentifier } from "zustand";
import { HasLastModified, Store } from "./AutoLastModifiedTypes";
type AutoLastModifiedDate = <
T extends HasLastModified,
Mps extends [StoreMutatorIdentifier, unknown][] = [],
Mcs extends [StoreMutatorIdentifier, unknown][] = []
>(
stateCreator: StateCreator<T, Mps, Mcs>
) => StateCreator<T, Mps, Mcs>;
type AutoLastModifiedDateImpl = <T extends HasLastModified>(
stateCreator: StateCreator<T, [], []>
) => StateCreator<T, [], []>;
type SetterFn<T> = (state: T) => T | Partial<T>;
const impl: AutoLastModifiedDateImpl = (stateCreator) => (set, get, store) => {
const newSet: typeof set = function (...args) {
let newState = args[0];
if (isSetterFunction(newState)) {
newState = newState(get());
}
const newResult = { ...newState, lastModifiedAt: Date.now() };
set(newResult, args[1]);
};
store.setState = newSet;
return stateCreator(newSet, get, store);
};
const isSetterFunction = function <T>(setter: T | Partial<T> | SetterFn<T>): setter is SetterFn<T> {
return (setter as SetterFn<T>).apply !== undefined;
};
export const autoLastModifiedDate = impl as AutoLastModifiedDate;
//--
type StoreWithLastModified = Store & HasLastModified;
const stateCreator: StateCreator<StoreWithLastModified, [], []> = (set) => ({
count: 0,
lastModifiedAt: undefined,
increaseCounter: (by: number) => set((state) => ({ count: state.count + by }))
});
const useStore = create<StoreWithLastModified>()(autoLastModifiedDate(stateCreator));
export default useStore;
选项 2:在商店本身拥有 lastModifiedAt 属性
基于 zustand 文档中的 this article。在这里,我们将 lastModifiedAt 属性存储在某种形式中metadata或者统计数据对象,直接位于商店中。请注意,将 lastModifiedAt 属性直接保留在商店中而不封装 meta 对象是行不通的。尽管属性已更新,但对任何人都看不到任何更改“外部消费者”.
import { create, Mutate, StateCreator, StoreApi, StoreMutatorIdentifier } from "zustand";
import { HasLastModified, Store } from "./AutoLastModifiedTypes";
type Write<T extends object, U extends object> = Omit<T, keyof U> & U;
type Cast<T, U> = T extends U ? T : U;
declare module "zustand" {
interface StoreMutators<S, A> {
autoLastModifiedDate: Write<Cast<S, object>, { meta: HasLastModified }>;
}
}
type AutoLastModifiedDate = <
T,
Mps extends [StoreMutatorIdentifier, unknown][] = [],
Mcs extends [StoreMutatorIdentifier, unknown][] = []
>(
stateCreator: StateCreator<T, [...Mps, ["autoLastModifiedDate", HasLastModified]], Mcs>,
initialValue?: HasLastModified
) => StateCreator<T, Mps, [["autoLastModifiedDate", HasLastModified], ...Mcs]>;
type AutoLastModifiedDateImpl = <T>(
stateCreator: StateCreator<T, [], []>,
initialValue?: HasLastModified
) => StateCreator<T, [], []>;
const impl: AutoLastModifiedDateImpl = (stateCreator, initialValue) => (set, get, _store) => {
type T = ReturnType<typeof stateCreator>;
const store = _store as Mutate<StoreApi<T>, [["autoLastModifiedDate", HasLastModified]]>;
store.meta = initialValue || {};
const newSet: typeof set = function (...args) {
store.meta.lastModifiedAt = Date.now();
set(...args);
};
store.setState = newSet;
return stateCreator(newSet, get, _store);
};
export const autoLastModifiedDate = impl as AutoLastModifiedDate;
//--
const stateCreator: StateCreator<Store, [], []> = (set) => ({
count: 0,
increaseCounter: (by: number) => set((state) => ({ count: state.count + by }))
});
const useStore = create<Store>()(autoLastModifiedDate(stateCreator));
useStore.subscribe(() => console.log(`Type store change detected. ${useStore.meta.lastModifiedAt || " not yet"}`));
export default useStore;
使用商店
使用这些商店的组件可能看起来像这样
import useTypeStore from "./AutoLastModifiedTypeStore";
import useStateStore from "./AutoLastModifiedStateStore";
const AutoLastModifiedCounter = () => {
const increaseTypeStoreCounter = useTypeStore((state) => state.increaseCounter);
const typeStoreCount = useTypeStore((state) => state.count);
const typeStorelastModifiedAt = useTypeStore.meta.lastModifiedAt;
const increaseStateStoreCounter = useStateStore((state) => state.increaseCounter);
const stateStoreCount = useStateStore((state) => state.count);
const stateStoreLastModifiedAt = useStateStore((state) => state.lastModifiedAt);
return (
<div>
<div>
Type store. Count = {typeStoreCount}, last modified at = {typeStorelastModifiedAt}
<button onClick={() => increaseTypeStoreCounter(1)}>Increment Type Store</button>
</div>
<div>
State store. Count = {stateStoreCount}, last modified at = {stateStoreLastModifiedAt}
<button onClick={() => increaseStateStoreCounter(1)}>Increment State Store</button>
</div>
</div>
);
};
export default AutoLastModifiedCounter;