diff --git a/stores/README.md b/stores/README.md new file mode 100644 index 0000000..355bb7d --- /dev/null +++ b/stores/README.md @@ -0,0 +1,178 @@ +# WellNuo Stores + +State management using Zustand for performant, scalable React state. + +## Why Zustand? + +Zustand provides several advantages over Context API: + +1. **Better Performance** - Only components using specific values re-render +2. **Simpler API** - No provider wrappers needed +3. **TypeScript First** - Excellent type inference +4. **Flexible** - Can be used outside React components +5. **Smaller Bundle** - Lightweight library (~1KB) + +## Available Stores + +### Auth Store (`authStore.ts`) + +Manages authentication state and user sessions. + +**Features:** +- OTP authentication flow (email → OTP → verify) +- Automatic session checking on app start +- Auto-logout on 401 unauthorized +- User profile management +- Optimized selector hooks + +**Basic Usage:** + +```tsx +import { useAuthStore, initAuthStore } from '@/stores/authStore'; + +// 1. Initialize once at app start (in app/_layout.tsx) +export default function RootLayout() { + useEffect(() => { + initAuthStore(); + }, []); +} + +// 2. Use in components +function MyComponent() { + const { user, isAuthenticated, logout } = useAuthStore(); + + return ( + + {isAuthenticated ? ( + Welcome {user?.firstName} + ) : ( + Please login + )} + + ); +} +``` + +**Optimized Selectors:** + +Use selector hooks when you only need specific values - prevents unnecessary re-renders: + +```tsx +import { useAuthUser, useIsAuthenticated } from '@/stores/authStore'; + +function UserProfile() { + // Only re-renders when user changes + const user = useAuthUser(); + + return {user?.email}; +} + +function LoginButton() { + // Only re-renders when isAuthenticated changes + const isAuthenticated = useIsAuthenticated(); + + return isAuthenticated ? : ; +} +``` + +**Authentication Flow:** + +```tsx +function LoginScreen() { + const { checkEmail, requestOtp, verifyOtp, error } = useAuthStore(); + + // Step 1: Check if email exists + const emailResult = await checkEmail('user@example.com'); + + // Step 2: Request OTP code + const otpResult = await requestOtp('user@example.com'); + + // Step 3: Verify OTP and authenticate + const success = await verifyOtp('user@example.com', '123456'); + + if (success) { + // User is now authenticated! + // Navigate to dashboard + } +} +``` + +**State Updates:** + +```tsx +function ProfileScreen() { + const { updateUser } = useAuthStore(); + + // Update local state (doesn't call API) + updateUser({ firstName: 'John', lastName: 'Doe' }); + + // To persist to server, also call: + await api.updateProfile({ firstName: 'John', lastName: 'Doe' }); +} +``` + +**Outside React:** + +```tsx +import { useAuthStore } from '@/stores/authStore'; + +// In utility functions or non-component code +function isUserAuthenticated() { + return useAuthStore.getState().isAuthenticated; +} + +// Trigger logout from anywhere +function forceLogout() { + useAuthStore.getState().logout(); +} +``` + +## Testing + +All stores have comprehensive test coverage: + +```bash +# Run all store tests +npm test -- stores/ + +# Run specific store tests +npm test -- authStore.test.ts + +# With coverage +npm test -- --coverage stores/ +``` + +## Best Practices + +1. **Use Selectors** - Prefer `useAuthUser()` over `useAuthStore((state) => state.user)` +2. **Initialize Once** - Call `initAuthStore()` only once at app startup +3. **Granular Subscriptions** - Only subscribe to values you need +4. **Type Safety** - Store is fully typed, use TypeScript benefits +5. **Server Sync** - Remember to call API after updating store state + +## Migration from Context + +If migrating from AuthContext: + +**Before (Context):** +```tsx +const { user, isAuthenticated } = useAuth(); +``` + +**After (Zustand):** +```tsx +const { user, isAuthenticated } = useAuthStore(); +``` + +The API is almost identical! Main difference: no Provider wrapper needed. + +## Adding New Stores + +Follow the pattern in `authStore.ts`: + +1. Define state interface +2. Define actions interface +3. Create store with `create()` +4. Export store and selector hooks +5. Add comprehensive tests +6. Document in this README