mcpField

state-management

github.com/eddiebe147開発40 回インストール
リポジトリ

state-management について

React開発者向けの状態管理リファレンスです。サーバー、クライアント、フォーム、URLの状態に使うライブラリを示し、再利用可能なパターンを紹介します。

主な機能

  1. 01サーバー状態に TanStack Query
  2. 02クライアント状態に Zustand
  3. 03React Hook Form と Zod のパターン
  4. 04nuqs または searchParams による URL 状態管理
  5. 05クエリキーファクトリーとフック
  6. 06セレクターと状態の永続化

活用シーン

  1. 01Reactアプリの状態構成
  2. 02再利用可能なデータ取得フックの作成
  3. 03コンポーネントの不要な再レンダリングの削減
  4. 04フォーム状態の検証と管理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/eddiebe147/claude-settings --skill state-management

インストール後、Claude Code で/state-managementを入力して読み込まれたか確認します。

詳細

状態管理

考え方

  • サーバー状態 → TanStack Query
  • クライアント状態 → Zustand
  • フォーム状態 → React Hook Form + Zod
  • URL 状態 → nuqs または searchParams

TanStack Query

クエリーキーのファクトリー
export const userKeys = {
  all: ['users'] as const,
  lists: () => [...userKeys.all, 'list'] as const,
  list: (filters: Filters) => [...userKeys.lists(), filters] as const,
  details: () => [...userKeys.all, 'detail'] as const,
  detail: (id: string) => [...userKeys.details(), id] as const,
};
フックのパターン
export function useUsers(filters?: Filters) {
  return useQuery({
    queryKey: userKeys.list(filters ?? {}),
    queryFn: () => getUsers(filters),
  });
}

export function useUser(id: string) {
  return useQuery({
    queryKey: userKeys.detail(id),
    queryFn: () => getUser(id),
    enabled: !!id,
  });
}

export function useCreateUser() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: createUser,
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: userKeys.lists() });
    },
  });
}

export function useUpdateUser() {
  const queryClient = useQueryClient();
  return useMutation({
    mutationFn: ({ id, data }: { id: string; data: UpdateUserInput }) =>
      updateUser(id, data),
    onSuccess: (_, { id }) => {
      queryClient.invalidateQueries({ queryKey: userKeys.detail(id) });
      queryClient.invalidateQueries({ queryKey: userKeys.lists() });
    },
  });
}

Zustand

型付きストア
interface UIStore {
  sidebarOpen: boolean;
  theme: 'light' | 'dark';
  toggleSidebar: () => void;
  setTheme: (theme: 'light' | 'dark') => void;
}

export const useUIStore = create<UIStore>((set) => ({
  sidebarOpen: true,
  theme: 'light',
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
  setTheme: (theme) => set({ theme }),
}));
パフォーマンス:セレクターを使用する
// CORRECT - Only re-renders when sidebarOpen changes
const sidebarOpen = useUIStore((s) => s.sidebarOpen);

// WRONG - Re-renders on ANY state change
const { sidebarOpen } = useUIStore();
永続化ミドルウェア
import { persist } from 'zustand/middleware';

export const useSettingsStore = create<SettingsStore>()(
  persist(
    (set) => ({
      language: 'en',
      setLanguage: (language) => set({ language }),
    }),
    {
      name: 'settings-storage',
    }
  )
);
セレクターによる計算値
// Create a selector
const selectFilteredItems = (state: Store) =>
  state.items.filter(item => item.active);

// Use in component
const filteredItems = useStore(selectFilteredItems);

フォーム状態:React Hook Form + Zod

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const schema = z.object({
  name: z.string().min(1, 'Required'),
  email: z.string().email('Invalid email'),
});

type FormData = z.infer<typeof schema>;

export function UserForm() {
  const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
    resolver: zodResolver(schema),
  });

  const onSubmit = (data: FormData) => {
    // data is typed and validated
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} />
      {errors.name && <span>{errors.name.message}</span>}

      <input {...register('email')} />
      {errors.email && <span>{errors.email.message}</span>}

      <button type="submit">Submit</button>
    </form>
  );
}

コメント