【问题标题】:Zustand Middleware that modifies state automaticallyZustand 自动修改状态的中间件
【发布时间】:2023-02-21 00:40:13
【问题描述】:

我有一个 zustand 商店,我想创建一个中间件,它在我调用 set() 时自动存储当前日期。

我认为这可以像这样手动完成:

// store
create((set) => ({
  counter: 1,
  lastModifiedAt: null,
  increaseCounter: () =>
    set((s) => ({
      ...prev,
      lastModifiedAt: Date.now(), // <-- how to automate this using a middleware?
      counter: s.counter + 1
    })),
}));

因为 lastModifiedAt 应该在状态改变时设置,所以我认为中间件是可行的方法。

【问题讨论】:

    标签: typescript middleware zustand


    【解决方案1】:

    在 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;
    

    【讨论】:

      猜你喜欢
      • 2023-01-28
      • 1970-01-01
      • 1970-01-01
      • 2022-06-19
      • 2012-08-18
      • 2018-11-27
      • 2021-04-19
      • 2022-08-16
      • 1970-01-01
      相关资源
      最近更新 更多