💣

SvelteでReduxを使う

2024/05/12に公開

ちょっとだけ経由を説明しておくと
sveltekitでフルに活用し、プロダクトを開発していて、サーバ側のテストはjestでそのままできるが、クライアントのユニットテストがjestをいれるだけでできなくて、ESM対応をいろいろしないといけない、ロジックの部分を細かい関数に切り出して、テストすればいいが、それはそれでちょっとだけコード負いづらいし、stateの変化もテストしたいという気持ちもあったのが、ことの発端

詳細が知りたい方はスクラップに雑にまとめているので、ぜひ👇
https://zenn.dev/link/comments/a3c2de881f4ee3

自分なりに考えてみたreduxにすることで得られメリット

  • 今後別のフレームワークに移行したくなったときにもやりやすい
    • v4 -> v5にアップデート時にも影響受けにくい
  • 補修性が良くなる

自分なりに考えてみたデメリット

  • 記述量が多い
  • svelteで動かすには色々と書かないといけない

実際のざっくりしたコード貼っておくと👇のような感じ

Store.redux.ts
import { configureStore, createSlice } from '@reduxjs/toolkit';

export interface MenuFormStore {
	name: string;
}

const initialState: MenuFormStore = {
	name: '',
};

const menuFormSlice = createSlice({
	name: 'menuForm',
	initialState,
	reducers: {
		setName: (state, action: { payload: string }) => {
			state.name = action.payload;
		}
	}
});

const MenuFormActions = menuFormSlice.actions;
const menuFormStoreRedux = configureStore({
	reducer: menuFormSlice.reducer
});
const MenuFormDispatch = menuFormStoreRedux.dispatch;

export const MenuFormReduxStoreModule = {
	initialState,
	Actions: MenuFormActions,
	Store: menuFormStoreRedux,
	Dispatch: MenuFormDispatch
} as const;

このファイルはsvelte関係ないので、そのままjestでユニットテストが書ける。
ただ、これだけではsvelte上でreactiveにならないので、👇のようにsvelte/storeからderivedを使い読み込み専用のストアを別ファイルで定義する(同じファイルにすると、jestに怒られる)

Store.ts
import { onMount } from 'svelte';
import { writable, derived } from 'svelte/store';
import { MenuFormReduxStoreModule } from './MenuForm.redux';

const state = writable(MenuFormReduxStoreModule.initialState);

export const getMenuFormStore = () => {
	onMount(() =>
		MenuFormReduxStoreModule.Store.subscribe(() => {
			state.update(prev => ({ ...prev, ...MenuFormReduxStoreModule.Store.getState() }));
		})
	);
	return derived(state, $state => $state);
};

svelte内で使うと👇

Menu.svelte
<script lang="ts">
import { getMenuFormStore } from './Store';
import { MenuFormReduxStoreModule } from './Store.redux';

$: menuFormStore = getMenuFormStore();
const { Actions, DIspatch } = MenuFormReduxStoreModule;
</script>

<div>
{$menuFormStore.name}
<input on:input={e => DIspatch(Actions.setName(e.currentTarget.value))}/>
</div>

最後

恐らく同じ仕組みで、jotaiやzustandもできるはず、ここらへんはsvelte v5でいろいろ解消されているので、ありがたい。

Discussion