1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

zustandのmiddleware wrapperを作成してみました

Posted at

概要

Qiita初投稿です。
Reactのzustandで個人的によく使うmiddlewareである

  • devtools
  • immer
  • persist
    を毎回記述するのが面倒だったのと
    Chrome拡張機能であるReduxDevToolsでzustandのstoreを見てみると
    Action名が anonymous になり、以下のディスカッションでset時にAction名を指定できるとなっているがそれも面倒...ということでAction名の設定と上記middlewareをラップした関数を作成してみました。
    https://github.com/pmndrs/zustand/discussions/1668

環境

CodeSandBoxで作成してみました。
依存関係などはそちらをご覧ください。
https://codesandbox.io/p/devbox/zustand-wrapper-r8vvnv?file=%2Fsrc%2Fstore-wrapper.ts%3A76%2C26

ソース

CodeSandBoxの「/src/store-wrapper.ts」ファイルが作成した関数です。

createStoreとcreatePersistStoreの2つの関数を定義しています。
immerを使用していて、WritableDraftで値をsetするときには関数になるので、functionであればfunctionの内容をそのままAction名にしています。

import { create, type StateCreator } from "zustand";
import { PersistOptions } from "zustand/middleware";
import { persist } from "zustand/middleware";
import { devtools } from "zustand/middleware";
import { immer } from "zustand/middleware/immer";

export const createStore = <T extends object>(
  stateCreator: StateCreator<
    T,
    [["zustand/immer", never], ["zustand/devtools", never]],
    []
  >,
  name?: string,
) => {
  if (import.meta.env.DEV) {
    return create<T, [["zustand/devtools", never], ["zustand/immer", never]]>(
      devtools<T, [], [["zustand/immer", never]]>(
        immer<T, [["zustand/devtools", never]], []>((set, ...other) =>
          stateCreator(
            (args, rep) => {
              let actName = "";
              if (typeof args === "function") {
                actName = args.toString();
              } else {
                actName = `set ${Object.keys(args).join(", ")}`;
              }
              return set(args, rep, actName);
            },
            ...other,
          ),
        ),
        {
          name: name,
        },
      ),
    );
  } else {
    return create<T, [["zustand/immer", never]]>(
      immer<T, [], []>((...prop) => stateCreator(...prop)),
    );
  }
};

export const createPersistStore = <T extends object>(
	stateCreator: StateCreator<
		T,
		[
			['zustand/devtools', never],
			['zustand/persist', unknown],
			['zustand/immer', never]
		],
		[]
	>,
	opt: PersistOptions<T, unknown>
) => {
	if (import.meta.env.DEV) {
		return create<
			T,
			[
				['zustand/devtools', never],
				['zustand/persist', unknown],
				['zustand/immer', never]
			]
		>(
			devtools<T, [], [['zustand/persist', unknown], ['zustand/immer', never]]>(
				persist<
					T,
					[['zustand/devtools', never]],
					[['zustand/immer', never]],
					unknown
				>(
					immer<
						T,
						[['zustand/devtools', never], ['zustand/persist', unknown]],
						[]
					>((set, ...other) =>
						stateCreator((args, rep) => {
							let actName = ''
							if (typeof args === 'function') {
								actName = args.toString()
							} else {
								actName = `set ${Object.keys(args).join(', ')}`
							}

							return set(args, rep, actName)
						}, ...other)
					),
					opt
				),
				{
					name: opt.name
				}
			)
		)
	} else {
		return create<T, [['zustand/persist', unknown], ['zustand/immer', never]]>(
			persist(
				immer<T, [['zustand/persist', unknown]], []>((...props) =>
					stateCreator(...props)
				),
				opt
			)
		)
	}
}

まとめ

上記のようなソースコードを色々探してみたのですが、なかなか見つからなかったので自作してみました。
同じような悩みを抱えている方の参考になれば幸いです。
ここをこうしたらもっと良くなるよ!など、アドバイスがあれば教えてください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?