🏆

Zustand Slice PatternのついったーPollやってます

2024/07/15に公開

https://x.com/dai_shi/status/1812649338654617918

どれが一番好みですか?全てTypeScriptサポートしてます。

Bare (Bear) Zustand

import { create, StateCreator } from "zustand";

interface CountSlice {
  count: number;
  incCount: () => void;
  resetCount: () => void;
}

interface TextSlice {
  text: string;
  updateText: (newText: string) => void;
  resetText: () => void;
}

interface SharedSlice {
  reset: () => void;
}

const createCountSlice: StateCreator<CountSlice, [], [], CountSlice> = (
  set,
) => ({
  count: 0,
  incCount: () => set((state) => ({ count: state.count + 1 })),
  resetCount: () => set({ count: 0 }),
});

const createTextSlice: StateCreator<TextSlice, [], [], TextSlice> = (set) => ({
  text: "Hello",
  updateText: (text) => set({ text }),
  resetText: () => set({ text: "Hello" }),
});

const createSharedSlice: StateCreator<
  CountSlice & TextSlice,
  [],
  [],
  SharedSlice
> = (set, get) => ({
  reset: () => {
    get().resetCount();
    get().resetText();
  },
});

const useCounterStore = create<CountSlice & TextSlice & SharedSlice>()(
  (...a) => ({
    ...createCountSlice(...a),
    ...createTextSlice(...a),
    ...createSharedSlice(...a),
  }),
);

Zustand Lens

import { create } from "zustand";
import { withLenses, lens } from "@dhmk/zustand-lens";

interface CountSlice {
  value: number;
  inc: () => void;
  reset: () => void;
}

interface TextSlice {
  value: string;
  update: (newText: string) => void;
  reset: () => void;
}

const countSlice = lens<CountSlice>((set) => ({
  value: 0,
  inc: () => set((prev) => ({ value: prev.value + 1 })),
  reset: () => set({ value: 0 }),
}));

const textSlice = lens<TextSlice>((set) => ({
  value: "Hello",
  update: (newText) => set({ value: newText }),
  reset: () => set({ value: "Hello" }),
}));

const useCounterStore = create<{
  count: CountSlice;
  text: TextSlice;
  reset: () => void;
}>(
  withLenses((set, get) => ({
    count: countSlice,
    text: textSlice,
    reset: () => {
      get().count.reset();
      get().text.reset();
    },
  })),
);

Zustand Slices

import { create } from "zustand";
import { createSlice, withSlices } from "zustand-slices";

const countSlice = createSlice({
  name: "count",
  value: 0,
  actions: {
    incCount: () => (prev) => prev + 1,
    reset: () => () => 0,
  },
});

const textSlice = createSlice({
  name: "text",
  value: "Hello",
  actions: {
    updateText: (newText: string) => () => newText,
    reset: () => () => "Hello",
  },
});

const useCounterStore = create(withSlices(countSlice, textSlice));

Zustand Valtio

import { create } from "zustand";
import { withProxy } from "zustand-valtio";

const countSlice = {
  value: 0,
  inc() {
    this.value++;
  },
  reset() {
    this.value = 0;
  },
};

const textSlice = {
  value: "Hello",
  update(newText: string) {
    this.value = newText;
  },
  reset() {
    this.value = "Hello";
  },
};

const useCounterState = create(
  withProxy({
    count: countSlice,
    text: textSlice,
    reset() {
      this.count.reset();
      this.text.reset();
    },
  }),
);

TypeScript Playgroundのリンクはこちらか

https://github.com/pmndrs/zustand/discussions/2647

Zustand Valtioについて補足

Zustand Valtioはまさに昨日完成したもので、想像以上に面白いものに仕上がりました。

https://x.com/dai_shi/status/1812487475023208706

元々は、immerの代わりに使えないかなぁ、と思って始めたプロジェクトなのですが、これはかなり使い物になるんじゃないでしょうか。個人的には、thisは避けるのが基本スタンスなのですが、Valtioの場合はthisの相性が良い(というか、それ以外だとうまくいかない)ので、活用する気になってます。

https://github.com/zustandjs/zustand-valtio

Discussion