Instruction file imported from ilya2ssss/saas (
.cursor/rules/zustand.mdc). Copyright stays with the author.
description: globs: /*.tsx,/*.ts alwaysApply: false
Objective: Ensure generated Zustand stores are well-structured, performant, and used appropriately for managing global client-side state.
Core Principles:
- Simplicity: Keep stores focused and minimal.
- Client State Only: Use Zustand only for client-side UI state, configuration state, or short-lived state not suitable for server cache (TanStack Query).
- Selectivity: Optimize component re-renders by selecting only the necessary state slices.
Rules:
-
Store Definition:
- Define stores using
createfromzustand. - MUST use TypeScript to define the store's state shape (
interfaceortype) and actions. - Separate state and actions clearly within the
createfunction. -
interface MyState { count: number; increment: () => void; // Add other state properties and action signatures }
const useMyStore = create((set, get) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), // Access current state with get(): get().count }));
- Define stores using
-
Store Usage:
- Access state in components using the hook:
const count = useMyStore(state => state.count); - MUST use selectors (
state => state.slice) to subscribe components only to the specific state pieces they need. This prevents unnecessary re-renders when unrelated parts of the store change. - AVOID selecting the entire state object (
useMyStore()) unless the component genuinely needs access to multiple, frequently changing parts of the state. - Access actions similarly:
const increment = useMyStore(state => state.increment);
- Access state in components using the hook:
-
Store Organization:
- Create multiple, smaller stores focused on specific domains or features (e.g.,
useOwnerFilterStore,useUserSettingsStore,useNotificationsStore) rather than one monolithic store. - Place store definitions in a dedicated directory (e.g.,
/stores).
- Create multiple, smaller stores focused on specific domains or features (e.g.,
-
Middleware:
- Consider using
devtoolsmiddleware during development for easier debugging with Redux DevTools:create(devtools<MyState>(...)) - Use
persistmiddleware if state needs to be persisted across sessions (e.g., in localStorage), but use cautiously for non-sensitive UI preferences only.
- Consider using
-
Async Actions:
- Handle asynchronous operations within actions as needed, updating state using
set()upon completion or failure. -
// Example async action fetchData: async () => { set({ loading: true }); try { const data = await api.fetchSomeData(); set({ data, loading: false }); } catch (error) { set({ error, loading: false }); } }
* However, prefer TanStack Query for managing server state, including loading/error states related to API calls. Use Zustand actions primarily for orchestrating client-side state changes around async events if necessary. - Handle asynchronous operations within actions as needed, updating state using
-
AVOID Duplication:
- DO NOT store server state fetched via TanStack Query within Zustand stores. Use TanStack Query's cache as the source of truth for server data. Zustand is for UI/client state.