State Management
Overview
The app uses two state management approaches:
- Server state: React Query (TanStack Query v5) for API data
- Global state: React Context only — no Redux, Zustand, or other state libraries
React Query Configuration
Defined in src/providers/query-provider.tsx:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: __DEV__ ? 0 : 5 * 60 * 1000, // 5 min (0 in dev)
gcTime: __DEV__ ? 0 : 10 * 60 * 1000, // 10 min (0 in dev)
retry: 2,
refetchOnWindowFocus: false,
},
mutations: {
retry: 1,
},
},
});In development mode, stale and cache times are set to 0 so you always see fresh data.
Context Providers
Global Contexts (src/context/)
| Context | File | Hook | Purpose |
|---|---|---|---|
| Auth | auth-context.tsx | useAuth() | JWT token, user object, signIn/signOut/refreshUser |
| Feature Flags | feature-flag-context.tsx | useFeatureFlags() | Remote feature flags from featureFlags.php |
| Cities | city-context.tsx | useCities() | Available cities list from API, coordinate lookup |
| Toast | toast-context.tsx | useToast() | App-wide toast notifications (success/error/warning/info) |
| Navigation Guard | navigation-guard-context.tsx | useNavigationGuard() | Auth-gated route protection |
| Notification | notification-context.tsx | useNotifications() | FCM token, push permission, badge management |
| New-to-Feature | new-to-feature-context.tsx | — | Tracks first-time feature visits |
| Slideable Modal | slideable-modal-context.tsx | — | Shared modal state |
Feature-Specific Contexts
| Context | File | Hook | Purpose |
|---|---|---|---|
| Explore Filters | features/explore/context/explore-filters-context.tsx | useExploreFilters() | Filter values, sort option, persisted to AsyncStorage |
| Device City | features/explore/context/device-city-context.tsx | useDeviceCity() | Selected city for non-logged-in users |
| Explore Navigation | features/explore/context/explore-navigation-context.tsx | — | Explore screen navigation state |
Provider Hierarchy
Providers are nested in a specific order in src/app/_layout.tsx. See Architecture for the full tree.
Key ordering constraints:
AuthProviderwraps everything (includingQueryProvider) so auth state is available before queriesFeatureFlagProvidercomes afterQueryProvidersince it could use queriesCitiesProviderdepends onDeviceCityProviderNavigationGuardProviderdepends onAuthProviderfor route protection
Conventions
- No enums: Use maps/objects instead (though
ViewingStatusinsrc/features/viewings/types/viewingStatus.tsis a legacy exception) - Prefer interfaces over types for object shapes
- Descriptive boolean names:
isLoading,hasError,isUsaMode - Context hooks throw if used outside their provider
Last updated on