Back to list
eduardbar

zustand-4

by eduardbar

Multi-tenant SaaS CRM for local businesses

0🍴 0📅 Jan 22, 2026

SKILL.md


name: zustand-4 description: > Zustand 4 state management patterns for MiGestion. Trigger: When creating Zustand stores for state management. license: MIT metadata: author: migestion version: '1.0' scope: [web] auto_invoke: 'Writing Zustand stores' allowed-tools: Read, Edit, Write, Glob, Grep, Bash, WebFetch, WebSearch, Task

Store Pattern (REQUIRED)

import { create } from 'zustand';

interface ExampleState {
  // State
  items: Item[];
  isLoading: boolean;
  error: string | null;

  // Actions
  fetchItems: () => Promise<void>;
  addItem: (item: Item) => Promise<void>;
  clearError: () => void;
}

export const useExampleStore = create<ExampleState>((set, get) => ({
  // Initial state
  items: [],
  isLoading: false,
  error: null,

  // Actions
  fetchItems: async () => {
    try {
      set({ isLoading: true, error: null });
      const items = await exampleService.getItems();
      set({ items, isLoading: false });
    } catch (error) {
      set({ error: error.message, isLoading: false });
    }
  },

  addItem: async item => {
    const newItem = await exampleService.createItem(item);
    set(state => ({ items: [...state.items, newItem] }));
  },

  clearError: () => set({ error: null }),
}));

Usage in Components

function ExampleComponent() {
  const { items, isLoading, error, fetchItems } = useExampleStore();

  useEffect(() => {
    fetchItems();
  }, []);

  if (isLoading) return <Spinner />;
  if (error) return <Alert message={error} />;

  return (
    <div>
      {items.map(item => <ItemCard key={item.id} item={item} />)}
    </div>
  );
}

Selectors (Performance)

// ✅ Select specific state to prevent re-renders
const items = useExampleStore(state => state.items);
const isLoading = useExampleStore(state => state.isLoading);

// ✅ Selector with derived state
const activeItems = useExampleStore(state => state.items.filter(item => item.status === 'active'));

Update Pattern

// ✅ Append to array
addItem: (item) => set(state => ({ items: [...state.items, item] })),

// ✅ Update item in array
updateItem: (id, updates) => set(state => ({
  items: state.items.map(item =>
    item.id === id ? { ...item, ...updates } : item
  ),
})),

// ✅ Remove from array
removeItem: (id) => set(state => ({
  items: state.items.filter(item => item.id !== id),
})),

// ✅ Set directly
setName: (name) => set({ name }),

Async Actions

fetchData: async () => {
  try {
    set({ isLoading: true, error: null });
    const data = await service.getData();
    set({ data, isLoading: false });
  } catch (error) {
    set({ error: error.message, isLoading: false });
  }
},

get() for Derived State

getState: () => get().items,
totalCount: () => get().items.length,

// Using get() within actions
refreshAll: async () => {
  const currentFilters = get().filters;
  await fetchItems(currentFilters);
},

Multiple Actions in One Set

reset: () => set({
  items: [],
  isLoading: false,
  error: null,
  filters: DEFAULT_FILTERS,
}),

Action Return Values

createClient: async (data) => {
  try {
    set({ isSubmitting: true, error: null });
    const client = await service.createClient(data);
    set({ isSubmitting: false });
    return client; // Return for caller to use
  } catch (error) {
    set({ error: error.message, isSubmitting: false });
    throw error; // Rethrow for caller to handle
  }
},

Store Persistence

import { persist } from 'zustand/middleware';

export const useAuthStore = create(
  persist<AuthState>(
    (set, get) => ({
      user: null,
      token: null,
      login: async credentials => {
        const { user, token } = await authService.login(credentials);
        set({ user, token });
      },
      logout: () => set({ user: null, token: null }),
    }),
    {
      name: 'auth-storage',
    }
  )
);

Store Slices (Modular Stores)

// slices/items-slice.ts
export const createItemsSlice = (set: StateSetter, get: StateGetter) => ({
  items: [],
  fetchItems: async () => {
    const items = await service.getItems();
    set({ items });
  },
});

// slices/ui-slice.ts
export const createUISlice = (set: StateSetter) => ({
  isLoading: false,
  error: null,
  setLoading: isLoading => set({ isLoading }),
});

// Combined store
export const useStore = create<ItemsState & UIState>((set, get) => ({
  ...createItemsSlice(set, get),
  ...createUISlice(set),
}));

Typing Actions

interface ExampleState {
  // State
  items: Item[];

  // Actions
  fetchItems: () => Promise<void>;
  addItem: (item: Item) => Promise<void>;
  updateItem: (id: string, updates: Partial<Item>) => Promise<void>;
  removeItem: (id: string) => Promise<void>;
  clearItems: () => void;
}

TypeScript with Generics

interface ListState<T> {
  items: T[];
  selected: T | null;
  isLoading: boolean;
}

function createListStore<T>(initialItems: T[] = []) {
  return create<ListState<T>>(set => ({
    items: initialItems,
    selected: null,
    isLoading: false,
  }));
}

// Usage
const useClientsStore = createListStore<Client>();

Testing Stores

import { renderHook, act, waitFor } from '@testing-library/react';
import { useExampleStore } from './example.store';

describe('useExampleStore', () => {
  it('should fetch items', async () => {
    const { result } = renderHook(() => useExampleStore());

    await act(async () => {
      await result.current.fetchItems();
    });

    expect(result.current.items).toHaveLength(10);
  });
});

DevTools

import { devtools } from 'zustand/middleware';

export const useStore = create(
  devtools(
    (set, get) => ({
      // store implementation
    }),
    { name: 'MyStore' }
  )
);
  • migestion-web - MiGestion Web patterns
  • react-18 - React component patterns
  • typescript - TypeScript patterns

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon