State Management Implementation Guide
Patterns for implementing client state management using Zustand.
GUIDE-02: State Management Implementation Guide
Overview
This guide provides practical implementation patterns for managing client state using Zustand, a lightweight and efficient state management library for React Native applications. It covers store creation, usage in components, handling asynchronous logic, persistence, and best practices within the context of our application architecture.
When To Use
Use these patterns when managing state that is:
- Global: Accessible and potentially modified by multiple parts of the application (e.g., UI state, user preferences).
- Client-Side: Not directly tied to server data (e.g., modal visibility, theme settings).
- Synchronous: Changes are typically immediate and don't involve network requests (though async logic can be handled within stores).
- Persistent (Optional): Needs to survive app restarts (e.g., theme preference) using middleware like
persist.
Important: Server state (e.g., API data) should be managed by TanStack Query (GUIDE-01), not Zustand.
Implementation Patterns
1. Store Creation
Zustand stores are created using the create function, defining state and methods to update it in a single object. Each feature typically has its own store for modularity.
- Key Concepts:
- The
createfunction returns a hook (e.g.,useSettingsStore) for accessing state and methods - Stores are feature-specific, aligning with the modular architecture
- Use TypeScript interfaces for type safety
- The
2. Using Stores in Components
Access and update state using the store's hook in React components.
- Key Concepts:
- The store hook provides direct access to state and methods, simplifying component logic
- Components re-render only when subscribed state changes, optimizing performance
3. Asynchronous Logic (Use Sparingly)
While TanStack Query handles server-side async operations, Zustand can manage client-side async logic within store methods.
Example: Simulating a complex client-side task.
- Recommendation: Avoid using Zustand for server-side async operations; use TanStack Query instead.
4. Selectors for Optimized Access
Client State Management supports optimized component rendering through selectors, allowing components to subscribe to specific state slices.
- Benefit: Selectors ensure components only re-render when the selected state changes, improving performance
5. Persistence with Middleware
To persist state across app restarts, use the persist middleware with AsyncStorage.
- Key Concepts:
- The
persistmiddleware saves state to AsyncStorage - Use
partializeto specify which parts of the state to persist
- The
Common Challenges
- Multiple Stores: Organize multiple stores by feature (e.g.,
useSettingsStore,useUiStore) for clarity - State Mutations: Always use
setto update state; direct mutations won't trigger re-renders or persist changes - Overusing Stores: Avoid storing server state in Zustand; use TanStack Query for API data
Performance Considerations
- Fine-Grained Subscriptions: Use selectors to subscribe to specific state slices, reducing unnecessary re-renders
- Store Scope: Keep stores focused on specific domains to avoid large, complex state objects
- Middleware Overhead: Be cautious with middleware like
persist, as it may impact performance for large states
Examples
Managing UI State (Modal Visibility)
Best Practices
- (Do ✅) Use multiple feature-specific stores rather than one large global store
- (Do ✅) Leverage selectors for optimized component rendering
- (Don't ❌) Store server state or API responses in Zustand
- (Do ✅) Use TypeScript for type safety
- (Consider 🤔) Isolating store logic into separate files for better organization