State and Data Fetching
State Boundaries
Section titled “State Boundaries”| What | Where it lives |
|---|---|
| Auth token and user role | AuthContext (localStorage + React context) |
| Theme (dark/light) | ThemeContext |
| API responses and server state | TanStack React Query |
| Form state | React Hook Form (local to each form component) |
React Query Configuration
Section titled “React Query Configuration”The QueryClient is configured at the root level in App.tsx with these defaults:
const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 5 * 60 * 1000, // 5 minutes gcTime: 10 * 60 * 1000, // 10 minutes refetchOnWindowFocus: false, retry: 1, }, },});How API Calls Work
Section titled “How API Calls Work”All API requests use the VITE_API_BASE_URL env var as the base URL. The JWT token from AuthContext is added as a header:
const { token } = useAuth();
const { data } = useQuery({ queryKey: ["articles", page], queryFn: () => fetch(`${import.meta.env.VITE_API_BASE_URL}/article?page=${page}`, { headers: { Authorization: `Bearer ${token}` }, }).then((r) => r.json()),});Route Code Splitting
Section titled “Route Code Splitting”Pages are lazily imported to reduce initial bundle size:
const ArticlesPage = lazy(() => import("./pages/Articles"));A Suspense boundary at the router level shows a loading skeleton while the page chunk loads.