13 KiB
ADR-005: Frontend State Management and Server Cache Strategy
Date: 2025-12-12 Implementation Date: 2026-01-08
Status: Accepted and Fully Implemented (Phases 1-8 complete, 100% coverage)
Context
The frontend currently uses React's built-in hooks (useState, useEffect, useContext) for state management, as seen in useAuth.tsx. While effective for simple cases, this manual approach becomes complex and error-prone when fetching, caching, and synchronizing data with the server. It often leads to custom logic for loading states, errors, re-fetching, and optimistic updates.
Decision
We will adopt a dedicated library for managing server state, such as TanStack Query (formerly React Query) or SWR, for all server-side data fetching on the client. This will abstract away the complexities of caching, background re-validation, and request deduplication.
Consequences
Positive: Leads to a more performant, predictable, and simpler frontend codebase. Standardizes how the client-side communicates with the server and handles loading/error states. Improves user experience through intelligent caching. Negative: Introduces a new frontend dependency. Requires a learning curve for developers unfamiliar with the library. Requires refactoring of existing data-fetching logic.
Implementation Status
Phase 1: Infrastructure & Core Queries (✅ Complete - 2026-01-08)
Files Created:
- src/config/queryClient.ts - Global QueryClient configuration
- src/hooks/queries/useFlyersQuery.ts - Flyers data query
- src/hooks/queries/useWatchedItemsQuery.ts - Watched items query
- src/hooks/queries/useShoppingListsQuery.ts - Shopping lists query
Files Modified:
- src/providers/AppProviders.tsx - Added QueryClientProvider wrapper
- src/providers/FlyersProvider.tsx - Refactored to use TanStack Query
- src/providers/UserDataProvider.tsx - Refactored to use TanStack Query
- src/services/apiClient.ts - Added pagination params to fetchFlyers
Benefits Achieved:
- ✅ Removed ~150 lines of custom state management code
- ✅ Automatic caching of server data
- ✅ Background refetching for stale data
- ✅ React Query Devtools available in development
- ✅ Automatic data invalidation on user logout
- ✅ Better error handling and loading states
Phase 2: Remaining Queries (✅ Complete - 2026-01-08)
Files Created:
- src/hooks/queries/useMasterItemsQuery.ts - Master grocery items query
- src/hooks/queries/useFlyerItemsQuery.ts - Flyer items query
Files Modified:
- src/providers/MasterItemsProvider.tsx - Refactored to use TanStack Query
- src/hooks/useFlyerItems.ts - Refactored to use TanStack Query
Benefits Achieved:
- ✅ Removed additional ~50 lines of custom state management code
- ✅ Per-flyer item caching (items cached separately for each flyer)
- ✅ Longer cache times for infrequently changing data (master items)
- ✅ Automatic query disabling when dependencies are not met
Phase 3: Mutations (✅ Complete - 2026-01-08)
Files Created:
- src/hooks/mutations/useAddWatchedItemMutation.ts - Add watched item mutation
- src/hooks/mutations/useRemoveWatchedItemMutation.ts - Remove watched item mutation
- src/hooks/mutations/useCreateShoppingListMutation.ts - Create shopping list mutation
- src/hooks/mutations/useDeleteShoppingListMutation.ts - Delete shopping list mutation
- src/hooks/mutations/useAddShoppingListItemMutation.ts - Add shopping list item mutation
- src/hooks/mutations/useUpdateShoppingListItemMutation.ts - Update shopping list item mutation
- src/hooks/mutations/useRemoveShoppingListItemMutation.ts - Remove shopping list item mutation
- src/hooks/mutations/index.ts - Barrel export for all mutation hooks
Benefits Achieved:
- ✅ Standardized mutation pattern across all data modifications
- ✅ Automatic cache invalidation after successful mutations
- ✅ Built-in success/error notifications
- ✅ Consistent error handling
- ✅ Full TypeScript type safety
- ✅ Comprehensive documentation with usage examples
See: plans/adr-0005-phase-3-summary.md for detailed documentation
Phase 4: Hook Refactoring (✅ Complete)
Goal: Refactor user-facing hooks to use TanStack Query mutation hooks.
Files Modified:
- src/hooks/useWatchedItems.tsx - Refactored to use mutation hooks
- src/hooks/useShoppingLists.tsx - Refactored to use mutation hooks
- src/contexts/UserDataContext.ts - Clean read-only interface (no setters)
- src/providers/UserDataProvider.tsx - Uses query hooks, no setter stubs
Benefits Achieved:
- ✅ Both hooks now use TanStack Query mutations
- ✅ Automatic cache invalidation after mutations
- ✅ Consistent error handling via mutation hooks
- ✅ Clean context interface (read-only server state)
- ✅ Backward compatible API for hook consumers
Phase 5: Admin Features (✅ Complete)
Goal: Create query hooks for admin features.
Files Created:
- src/hooks/queries/useActivityLogQuery.ts - Activity log with pagination
- src/hooks/queries/useApplicationStatsQuery.ts - Application statistics
- src/hooks/queries/useSuggestedCorrectionsQuery.ts - Corrections data
- src/hooks/queries/useCategoriesQuery.ts - Categories (public endpoint)
Components Migrated:
- src/pages/admin/ActivityLog.tsx - Uses useActivityLogQuery
- src/pages/admin/AdminStatsPage.tsx - Uses useApplicationStatsQuery
- src/pages/admin/CorrectionsPage.tsx - Uses useSuggestedCorrectionsQuery, useMasterItemsQuery, useCategoriesQuery
Benefits Achieved:
- ✅ Automatic caching of admin data
- ✅ Parallel fetching (CorrectionsPage fetches 3 queries simultaneously)
- ✅ Consistent stale times (30s to 2 min based on data volatility)
- ✅ Shared cache across components (useMasterItemsQuery reused)
Phase 6: Analytics Features (✅ Complete - 2026-01-10)
Goal: Migrate analytics and deals features.
Files Created:
- src/hooks/queries/useBestSalePricesQuery.ts - Best sale prices for watched items
- src/hooks/queries/useFlyerItemsForFlyersQuery.ts - Batch fetch items for multiple flyers
- src/hooks/queries/useFlyerItemCountQuery.ts - Count items across flyers
Files Modified:
- src/pages/MyDealsPage.tsx - Now uses useBestSalePricesQuery
- src/hooks/useActiveDeals.tsx - Refactored to use TanStack Query hooks
Benefits Achieved:
- ✅ Removed useApi dependency from analytics features
- ✅ Automatic caching of deal data (2-5 minute stale times)
- ✅ Consistent error handling via TanStack Query
- ✅ Batch fetching for flyer items (single query for multiple flyers)
Phase 7: Cleanup (✅ Complete - 2026-01-10)
Goal: Remove legacy hooks once migration is complete.
Files Created:
- src/hooks/queries/useUserAddressQuery.ts - User address fetching
- src/hooks/queries/useAuthProfileQuery.ts - Auth profile fetching
- src/hooks/mutations/useGeocodeMutation.ts - Address geocoding
Files Modified:
- src/hooks/useProfileAddress.ts - Refactored to use TanStack Query
- src/providers/AuthProvider.tsx - Refactored to use TanStack Query
Files Removed:
src/hooks/useApi.ts- Legacy hook removedsrc/hooks/useApi.test.ts- Test file removedsrc/hooks/useApiOnMount.ts- Legacy hook removedsrc/hooks/useApiOnMount.test.ts- Test file removed
Benefits Achieved:
- ✅ Removed all legacy
useApianduseApiOnMounthooks - ✅ Complete TanStack Query coverage for all data fetching
- ✅ Consistent error handling across the entire application
- ✅ Unified caching strategy for all server state
Phase 8: Additional Component Migration (✅ Complete - 2026-01-10)
Goal: Migrate remaining components with manual data fetching to TanStack Query.
Files Created:
- src/hooks/queries/useUserProfileDataQuery.ts - Combined user profile + achievements query
- src/hooks/queries/useLeaderboardQuery.ts - Public leaderboard data
- src/hooks/queries/usePriceHistoryQuery.ts - Historical price data for watched items
Files Modified:
- src/hooks/useUserProfileData.ts - Refactored to use useUserProfileDataQuery
- src/components/Leaderboard.tsx - Refactored to use useLeaderboardQuery
- src/features/charts/PriceHistoryChart.tsx - Refactored to use usePriceHistoryQuery
Benefits Achieved:
- ✅ Parallel fetching for profile + achievements data
- ✅ Public leaderboard cached with 2-minute stale time
- ✅ Price history cached with 10-minute stale time (data changes infrequently)
- ✅ Backward-compatible setProfile function via queryClient.setQueryData
- ✅ Stable query keys with sorted IDs for price history
Migration Status
Current Coverage: 100% complete
| Category | Total | Migrated | Status |
|---|---|---|---|
| Query Hooks (User) | 7 | 7 | ✅ 100% |
| Query Hooks (Admin) | 4 | 4 | ✅ 100% |
| Query Hooks (Analytics) | 3 | 3 | ✅ 100% |
| Query Hooks (Phase 8) | 3 | 3 | ✅ 100% |
| Mutation Hooks | 8 | 8 | ✅ 100% |
| User Hooks | 2 | 2 | ✅ 100% |
| Analytics Features | 2 | 2 | ✅ 100% |
| Component Migration (Phase 8) | 3 | 3 | ✅ 100% |
| Legacy Hook Cleanup | 4 | 4 | ✅ 100% |
Completed:
- ✅ Core query hooks (flyers, flyerItems, masterItems, watchedItems, shoppingLists)
- ✅ Admin query hooks (activityLog, applicationStats, suggestedCorrections, categories)
- ✅ Analytics query hooks (bestSalePrices, flyerItemsForFlyers, flyerItemCount)
- ✅ Auth/Profile query hooks (authProfile, userAddress)
- ✅ Phase 8 query hooks (userProfileData, leaderboard, priceHistory)
- ✅ All mutation hooks (watched items, shopping lists, geocode)
- ✅ Provider refactoring (AppProviders, FlyersProvider, MasterItemsProvider, UserDataProvider, AuthProvider)
- ✅ User hooks refactoring (useWatchedItems, useShoppingLists, useProfileAddress, useUserProfileData)
- ✅ Admin component migration (ActivityLog, AdminStatsPage, CorrectionsPage)
- ✅ Analytics features (MyDealsPage, useActiveDeals)
- ✅ Component migration (Leaderboard, PriceHistoryChart)
- ✅ Legacy hooks removed (useApi, useApiOnMount)
See plans/adr-0005-master-migration-status.md for complete tracking of all components.
Implementation Guide
See plans/adr-0005-implementation-plan.md for detailed implementation steps.