state-management
GitHub基于 TanStack Query 和 Mock JSON 的前端数据层方案,用于封装数据获取 Hook、管理本地缓存及连接组件状态。
Trigger Scenarios
Install
npx skills add github/replica --skill state-management -g -y
SKILL.md
Frontmatter
{
"name": "state-management",
"description": "CoreAI Sandbox's TanStack Query + mock-JSON data layer. USE WHEN: editing data-fetching hooks (`app\/hooks\/use*.ts`), adding mock data in `app\/data\/`, or wiring a component to query state."
}
State Management Instructions — CoreAI Sandbox Demo
Paths below are relative to this app's directory.
Overview
The app uses TanStack Query v5 hooks over local JSON data to provide client-side loading, filtering, and caching for prototypes.
Architecture
1. QueryProvider Setup
The app is wrapped with QueryProvider in app/root.tsx:
import { QueryProvider } from './core/datafetching/QueryProvider';
export default function App() {
return (
<QueryProvider>
<AppContent>
<Outlet />
</AppContent>
</QueryProvider>
);
}
Configuration (app/core/datafetching/QueryProvider.tsx):
staleTime: 5 minutes- Data is considered freshgcTime: 10 minutes- Garbage collection timeretry: false- Don't retry failed requestsrefetchOnWindowFocus: false- Don't refetch on focus
2. Mock Data Layer
Location: app/data/
Files:
agents.json- agents with varied lifecycle statusestools.json- tools with categories and publish statusmodels.json- models with provider and category metadatahome.json- Home page endpoints, model actions, carousel models, and recent workindex.ts- TypeScript interfaces and utility functions
Data Types:
export interface Agent {
id: string;
name: string;
description: string;
status: 'draft' | 'published' | 'deployed';
tags: string[];
owner: string;
createdDate: string; // ISO 8601
updatedDate: string; // ISO 8601
version: string; // Semantic version
}
export interface Tool {
mark: string;
initialsColor: string;
id: string;
name: string;
description: string;
category: ToolCategory;
isPublished: boolean;
}
Utility Functions:
// Filter agents by status
filterAgentsByStatus(agents: Agent[], status: AgentStatus): Agent[]
// Filter tools by category
filterToolsByCategory(tools: Tool[], category: ToolCategory): Tool[]
// Filter tools by published status
filterToolsByPublished(tools: Tool[], isPublished: boolean): Tool[]
3. Custom Hooks Pattern
Location: app/hooks/
Custom hooks wrap TanStack Query's useQuery and import JSON data directly.
Example — useAgents.ts:
import type { Agent, AgentStatus } from '~/data';
import { useQuery } from '@tanstack/react-query';
import { agents, filterAgentsByStatus } from '~/data';
async function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export interface UseAgentsOptions {
status?: AgentStatus;
}
export function useAgents(options?: UseAgentsOptions) {
return useQuery({
queryKey: ['agents', options?.status],
queryFn: async (): Promise<Agent[]> => {
// Simulate network delay (400ms)
await delay(400);
// Return filtered or all agents
if (options?.status) {
return filterAgentsByStatus(agents, options.status);
}
return agents;
},
});
}
Available Hooks:
useAgents(options?)- Fetch agents, optionally filtered by statususeTools(options?)- Fetch tools, optionally filtered by category/publish statususeHomeData()- Fetch mock home page content
Query Keys:
['agents', status?] // e.g., ['agents', 'draft']
['tools', category?, isPublished?] // e.g., ['tools', 'Data', true]
['home'] // Home page mock content
4. Component Usage Pattern
Components use custom hooks directly in route components:
import { useAgents } from '~/hooks/useAgents';
import { CollectionPage } from '~/components/common';
import type { TableColumnDefinition } from '~/components/common';
import type { Agent } from '~/data';
export default function BuildAgents() {
const { data: agents, isLoading } = useAgents({ status: 'draft' });
const columns: TableColumnDefinition<Agent>[] = [
{
columnId: 'name',
renderHeaderCell: () => 'Name',
renderCell: (agent) => agent.name,
compare: (first, second) => first.name.localeCompare(second.name),
},
];
return (
<CollectionPage
headerProps={{ title: 'Agents' }}
dataGridProps={{
items: agents ?? [],
isLoading,
columns,
getRowId: (agent) => agent.id,
}}
/>
);
}
5. Route-Specific Filtering
Different routes show different subsets of the same data:
| Route | Hook Usage | Filter |
|---|---|---|
/build/agents |
useAgents() via AgentList |
All agents (50) |
/build/agents/drafts |
useAgents() via AgentList, then local status filtering |
Draft agents (14) |
/build/agents/published |
useAgents() via AgentList, then local status filtering |
Published agents (19) |
/operate/agents |
useAgents({ status: 'deployed' }) |
Deployed agents (17) |
/build/tools |
useTools() |
All tools (35) |
/discover/tools |
Direct tools import mapped into catalog items |
All tools (35; 29 published) |
6. Loading States
TanStack Query provides automatic loading state management:
import { DataGridWithFilters } from '~/components/common';
const { data, isLoading, error } = useAgents();
// The app-owned grid handles loading state; columns are defined by the consuming component.
<DataGridWithFilters columns={columns} isLoading={isLoading} items={data ?? []} />;
States Available:
isLoading- Initial fetch in progressisFetching- Any fetch in progress (including background refetch)isError- Query failederror- Error object if query faileddata- Query result data
Best Practices
DO:
- ✅ Use TanStack Query for ALL data fetching
- ✅ Use custom hooks (
useAgents,useTools) in components - ✅ Pass query results to the app-owned
DataGridWithFilters(or a page composition'sdataGridProps) withisLoading; the underlying CoreAIDataGriddoes not supply this app-owned loading API - ✅ Provide sensible empty states
- ✅ Use consistent query keys for caching
- ✅ Filter data via hook options when possible
DON'T:
- ❌ Don't fetch data in
useEffect - ❌ Don't use
useStatefor server data - ❌ Don't call TanStack Query's
useQuerydirectly - use custom hooks - ❌ Don't mutate imported JSON data
- ❌ Don't add SSR/loader patterns (this is client-only)
- ❌ Don't add real API calls or authentication
Simulated Delays
The mock query hooks useAgents, useTools, and useHomeData include a 400ms simulated delay:
async function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
This creates realistic loading states without actual network requests.
Adding New Data
1. Create JSON File
// app/data/newentity.json
[
{ "id": "entity-001", "name": "Example", ... }
]
2. Add Type to app/data/index.ts
export interface NewEntity {
id: string;
name: string;
// ...
}
import newEntityData from './newentity.json';
export const newEntities: NewEntity[] = newEntityData as NewEntity[];
3. Create Custom Hook
// app/hooks/useNewEntity.ts
import { useQuery } from '@tanstack/react-query';
import { newEntities } from '~/data';
async function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export function useNewEntity() {
return useQuery({
queryKey: ['newEntity'],
queryFn: async () => {
await delay(400);
return newEntities;
},
});
}
4. Use in Component
import { useNewEntity } from '~/hooks/useNewEntity';
const { data, isLoading } = useNewEntity();
Architecture Benefits
This simplified architecture provides:
- Consistent Patterns - Easy to port components to production
- Realistic UX - Loading states, caching behavior
- Type Safety - TypeScript throughout
- Low Complexity - No backend needed
- Fast Iteration - Change JSON, see results immediately
Migration to Production
When moving components to production:
- Custom hooks stay similar but fetch from real APIs
- Add resolver pattern and authentication
- Add SSR/loader prefetching
- Replace JSON imports with HTTP calls
- Add proper error handling and retry logic
- Configure appropriate
staleTimeper query
Related Files
app/core/datafetching/QueryProvider.tsx- TanStack Query providerapp/data/index.ts- Type definitions and exportsapp/data/agents.json- Mock agent dataapp/data/tools.json- Mock tool dataapp/data/models.json- Mock model dataapp/data/home.json- Mock home contentapp/hooks/useAgents.ts- Agents data hookapp/hooks/useTools.ts- Tools data hookapp/hooks/useHomeData.ts- Home content data hook
Version History
- f19c050 Current 2026-11-07 08:51


