Client State Management
Relevant source files
The following files were used as context for generating this wiki page:Client-side state management in the adfluens-app is a multi-layered system designed for robustness and performance. It primarily leverages a combination of React's built-in state, TanStack Query for server state caching, and React Context for providing global state like authentication and theming. This approach separates concerns, ensuring that server data, UI state, and local component state are handled by the most appropriate tools.
The architecture is centered around a hierarchy of providers that wrap the main application. This ensures that all components have access to necessary state management clients and data. Data fetching is standardized through tRPC and TanStack Query, providing type-safe API communication and efficient caching, while transient UI state is managed locally within components or through dedicated context providers.
Core Technologies
The client state management strategy relies on a few key libraries working in concert.
| Library/Tool | Role |
|---|---|
| TanStack Query | Manages server state, including data fetching, caching, synchronization, and background updates. |
| tRPC | Provides a type-safe API client for communication between the frontend and backend. |
| React Context | Used to provide global state and functionality, such as theme and authentication session. |
| React State | (useState, useEffect) Manages local, component-level state that doesn't need to be global. |
| LocalStorage | Persists non-critical data across browser sessions to improve user experience and reduce API calls. |
Sources: README.md, apps/web/package.json, apps/web/src/features/ai/components/video-analyzer.component.tsx:44-49
The following diagram illustrates the different layers of state management in the client application.
Provider Architecture
The entire application is wrapped in a series of providers that initialize and expose the necessary state management clients and contexts. The nesting order is critical for ensuring dependencies are available where needed.
Sources: apps/web/src/app.tsx:39-69
This diagram shows the hierarchy of providers as defined in app.tsx.
Sources: apps/web/src/app.tsx:39-69
Provider Details
| Provider | Purpose | Configuration Source |
|---|---|---|
ThemeProvider | Manages light/dark/system theme. | apps/web/src/app.tsx:40 |
QueryClientProvider | Provides the TanStack Query client for server state management. | apps/web/src/app.tsx:21-29, 41 |
TRPCProvider | Connects TanStack Query with the tRPC client for type-safe API calls. | apps/web/src/app.tsx:42 |
AuthProvider | Manages user authentication state and session, likely using better-auth. | apps/web/src/app.tsx:43 |
TooltipProvider | Enables Radix UI tooltips throughout the application. | apps/web/src/app.tsx:44 |
Server State (TanStack Query & tRPC)
Server state, which includes all data fetched from the backend, is exclusively managed by TanStack Query in conjunction with tRPC.
tRPC Client Setup
A singleton trpcClient is created and configured to communicate with the backend API. It is integrated with TanStack Query through the @trpc/tanstack-react-query package.
Sources: apps/web/src/lib/trpc.ts
Query Client Configuration
The QueryClient is instantiated with default options that apply to all queries in the application, such as a staleTime of 5 minutes to reduce redundant fetching.
// File: apps/web/src/app.tsx:21-29
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
retry: 1,
},
},
});
Data Fetching Flow
Components use the trpcClient to perform queries and mutations. TanStack Query automatically handles the request lifecycle, caching, and state updates. The VideoAnalyzer component demonstrates this pattern for both mutations (trpcClient.ai.analyze.mutate) and subsequent chat interactions (trpcClient.ai.chat.mutate).
The sequence diagram below shows a typical data mutation flow initiated from a React component.
Local & Persistent State
While server state is handled globally, local UI state and persisted data are managed at the component level.
Local Component State (useState)
Standard React hooks like useState are used for managing state that is local to a single component and does not need to be shared. This includes UI flags, form inputs, and error messages.
Examples from VideoAnalyzer:
loading: A boolean to show a spinner during analysis.error: A string to display an error message if the API call fails.chatInput: The current value of the chat input field.chatMessages: An array of chat messages for the interactive assistant.
Sources: apps/web/src/features/ai/components/video-analyzer.component.tsx:33-41
Persistent State (localStorage)
To improve performance and avoid re-fetching expensive data, localStorage is used to cache the results of AI analysis. The VideoAnalyzer component first checks for a saved analysis in localStorage before triggering a new API call.
This flowchart illustrates the logic for loading the AI analysis data.
UI State (Theming)
The application's theme (light/dark mode) is a global UI state managed by ThemeProvider and persisted in localStorage.
- Provider: The
<ThemeProvider defaultTheme="system">inapp.tsxinitializes the theme management. - FOUC Prevention: A script in
index.htmlruns before React loads to apply the correct theme class (lightordark) to the<html>element, preventing a "flash of unstyled content". - Interaction: Components like
ThemeToggleallow the user to change the theme. It receives the currentthemeand anonThemeChangecallback to update the state.
Sources: apps/web/src/app.tsx:40, apps/web/index.html:5-17, packages/ui/src/components/theme-toggle.tsx
Authentication State
Authentication state is managed by the AuthProvider and components within the features/auth directory.
- Provider: The
<AuthProvider>inapp.tsxwraps all protected routes, managing the user's session. - UI Feedback: The
AuthFormcomponent uses local state (isLoading,error) to provide immediate feedback to the user during the sign-in or sign-up process. This state is managed internally and is not part of the global auth context.
Sources: apps/web/src/app.tsx:43, apps/web/src/features/auth/components/auth-form.component.tsx:23-24