Zustand 4 state management patterns for MiGestion. Trigger: When creating Zustand stores for state management.
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 }),
}));
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>
);
}
// ✅ 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'));
// ✅ 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 }),
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 });
}
},
getState: () => get().items,
totalCount: () => get().items.length,
// Using get() within actions
refreshAll: async () => {
const currentFilters = get().filters;
await fetchItems(currentFilters);
},