Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 1m1s
245 lines
13 KiB
Markdown
245 lines
13 KiB
Markdown
# 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](../../src/config/queryClient.ts) - Global QueryClient configuration
|
|
- [src/hooks/queries/useFlyersQuery.ts](../../src/hooks/queries/useFlyersQuery.ts) - Flyers data query
|
|
- [src/hooks/queries/useWatchedItemsQuery.ts](../../src/hooks/queries/useWatchedItemsQuery.ts) - Watched items query
|
|
- [src/hooks/queries/useShoppingListsQuery.ts](../../src/hooks/queries/useShoppingListsQuery.ts) - Shopping lists query
|
|
|
|
**Files Modified:**
|
|
|
|
- [src/providers/AppProviders.tsx](../../src/providers/AppProviders.tsx) - Added QueryClientProvider wrapper
|
|
- [src/providers/FlyersProvider.tsx](../../src/providers/FlyersProvider.tsx) - Refactored to use TanStack Query
|
|
- [src/providers/UserDataProvider.tsx](../../src/providers/UserDataProvider.tsx) - Refactored to use TanStack Query
|
|
- [src/services/apiClient.ts](../../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](../../src/hooks/queries/useMasterItemsQuery.ts) - Master grocery items query
|
|
- [src/hooks/queries/useFlyerItemsQuery.ts](../../src/hooks/queries/useFlyerItemsQuery.ts) - Flyer items query
|
|
|
|
**Files Modified:**
|
|
|
|
- [src/providers/MasterItemsProvider.tsx](../../src/providers/MasterItemsProvider.tsx) - Refactored to use TanStack Query
|
|
- [src/hooks/useFlyerItems.ts](../../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](../../src/hooks/mutations/useAddWatchedItemMutation.ts) - Add watched item mutation
|
|
- [src/hooks/mutations/useRemoveWatchedItemMutation.ts](../../src/hooks/mutations/useRemoveWatchedItemMutation.ts) - Remove watched item mutation
|
|
- [src/hooks/mutations/useCreateShoppingListMutation.ts](../../src/hooks/mutations/useCreateShoppingListMutation.ts) - Create shopping list mutation
|
|
- [src/hooks/mutations/useDeleteShoppingListMutation.ts](../../src/hooks/mutations/useDeleteShoppingListMutation.ts) - Delete shopping list mutation
|
|
- [src/hooks/mutations/useAddShoppingListItemMutation.ts](../../src/hooks/mutations/useAddShoppingListItemMutation.ts) - Add shopping list item mutation
|
|
- [src/hooks/mutations/useUpdateShoppingListItemMutation.ts](../../src/hooks/mutations/useUpdateShoppingListItemMutation.ts) - Update shopping list item mutation
|
|
- [src/hooks/mutations/useRemoveShoppingListItemMutation.ts](../../src/hooks/mutations/useRemoveShoppingListItemMutation.ts) - Remove shopping list item mutation
|
|
- [src/hooks/mutations/index.ts](../../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](../../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](../../src/hooks/useWatchedItems.tsx) - Refactored to use mutation hooks
|
|
- [src/hooks/useShoppingLists.tsx](../../src/hooks/useShoppingLists.tsx) - Refactored to use mutation hooks
|
|
- [src/contexts/UserDataContext.ts](../../src/contexts/UserDataContext.ts) - Clean read-only interface (no setters)
|
|
- [src/providers/UserDataProvider.tsx](../../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](../../src/hooks/queries/useActivityLogQuery.ts) - Activity log with pagination
|
|
- [src/hooks/queries/useApplicationStatsQuery.ts](../../src/hooks/queries/useApplicationStatsQuery.ts) - Application statistics
|
|
- [src/hooks/queries/useSuggestedCorrectionsQuery.ts](../../src/hooks/queries/useSuggestedCorrectionsQuery.ts) - Corrections data
|
|
- [src/hooks/queries/useCategoriesQuery.ts](../../src/hooks/queries/useCategoriesQuery.ts) - Categories (public endpoint)
|
|
|
|
**Components Migrated:**
|
|
|
|
- [src/pages/admin/ActivityLog.tsx](../../src/pages/admin/ActivityLog.tsx) - Uses useActivityLogQuery
|
|
- [src/pages/admin/AdminStatsPage.tsx](../../src/pages/admin/AdminStatsPage.tsx) - Uses useApplicationStatsQuery
|
|
- [src/pages/admin/CorrectionsPage.tsx](../../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](../../src/hooks/queries/useBestSalePricesQuery.ts) - Best sale prices for watched items
|
|
- [src/hooks/queries/useFlyerItemsForFlyersQuery.ts](../../src/hooks/queries/useFlyerItemsForFlyersQuery.ts) - Batch fetch items for multiple flyers
|
|
- [src/hooks/queries/useFlyerItemCountQuery.ts](../../src/hooks/queries/useFlyerItemCountQuery.ts) - Count items across flyers
|
|
|
|
**Files Modified:**
|
|
|
|
- [src/pages/MyDealsPage.tsx](../../src/pages/MyDealsPage.tsx) - Now uses useBestSalePricesQuery
|
|
- [src/hooks/useActiveDeals.tsx](../../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](../../src/hooks/queries/useUserAddressQuery.ts) - User address fetching
|
|
- [src/hooks/queries/useAuthProfileQuery.ts](../../src/hooks/queries/useAuthProfileQuery.ts) - Auth profile fetching
|
|
- [src/hooks/mutations/useGeocodeMutation.ts](../../src/hooks/mutations/useGeocodeMutation.ts) - Address geocoding
|
|
|
|
**Files Modified:**
|
|
|
|
- [src/hooks/useProfileAddress.ts](../../src/hooks/useProfileAddress.ts) - Refactored to use TanStack Query
|
|
- [src/providers/AuthProvider.tsx](../../src/providers/AuthProvider.tsx) - Refactored to use TanStack Query
|
|
|
|
**Files Removed:**
|
|
|
|
- ~~src/hooks/useApi.ts~~ - Legacy hook removed
|
|
- ~~src/hooks/useApi.test.ts~~ - Test file removed
|
|
- ~~src/hooks/useApiOnMount.ts~~ - Legacy hook removed
|
|
- ~~src/hooks/useApiOnMount.test.ts~~ - Test file removed
|
|
|
|
**Benefits Achieved:**
|
|
|
|
- ✅ Removed all legacy `useApi` and `useApiOnMount` hooks
|
|
- ✅ 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](../../src/hooks/queries/useUserProfileDataQuery.ts) - Combined user profile + achievements query
|
|
- [src/hooks/queries/useLeaderboardQuery.ts](../../src/hooks/queries/useLeaderboardQuery.ts) - Public leaderboard data
|
|
- [src/hooks/queries/usePriceHistoryQuery.ts](../../src/hooks/queries/usePriceHistoryQuery.ts) - Historical price data for watched items
|
|
|
|
**Files Modified:**
|
|
|
|
- [src/hooks/useUserProfileData.ts](../../src/hooks/useUserProfileData.ts) - Refactored to use useUserProfileDataQuery
|
|
- [src/components/Leaderboard.tsx](../../src/components/Leaderboard.tsx) - Refactored to use useLeaderboardQuery
|
|
- [src/features/charts/PriceHistoryChart.tsx](../../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](../../plans/adr-0005-master-migration-status.md) for complete tracking of all components.
|
|
|
|
## Implementation Guide
|
|
|
|
See [plans/adr-0005-implementation-plan.md](../../plans/adr-0005-implementation-plan.md) for detailed implementation steps.
|