一键导入
client-state-management
Use when adding or changing client state, React Query data flow, immutable context values, or Zustand stores.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when adding or changing client state, React Query data flow, immutable context values, or Zustand stores.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when adding, changing, extracting, or reusing client components, including Storybook stories and controls.
Use when adding or changing forms in apps/client.
Use when changing GitHub OAuth, session cookies, SessionGuard, logout, or authenticated API request identity.
Use when adding or changing routes in apps/client.
Use when styling apps/client with Tailwind CSS v4.
Use when adding or changing client API query or mutation hooks.
| name | client-state-management |
| description | Use when adding or changing client state, React Query data flow, immutable context values, or Zustand stores. |
Generated from ai/registry.json. Do not edit manually.
Canonical skill: ../../../ai/skills/client-state-management.md.
Referenced context:
../../../ai/rules/client-state-rules.md../../../ai/rules/client-api-hook-rules.md../../../ai/architecture/client-app.md../../../ai/examples/good-client-api-operation.mdThis file is compiled from canonical AI knowledge files. Edit canonical files under ai, then run npm run ai:sync.
ai/skills/client-state-management.mdUse this skill when adding or changing client-side state, React Query data flow, immutable context values, or Zustand stores in apps/client.
Place each state concern at the narrowest correct owner without duplicating server data or creating mutable global context.
ai/rules/client-state-rules.mdai/rules/client-api-hook-rules.mdai/architecture/client-app.mdai/examples/good-client-api-operation.mduseState or Zustand.npm --workspace @capture-flag/client run build.ai/rules/client-state-rules.mdRules for state ownership in apps/client.
queryClient.invalidateQueries.useRouteContext to derive selected organization, project, config, and environment from route params, search params, and React Query data.useCollectionSelection for page-local collection selection that does not need to be linkable.src/core/hooks/<hook>.ts and import them directly from that file.src/api/<domain> for server/cache state.useRouteContext for selected route resources derived from navigation state plus server data.useState or React Hook Form state for component-only state.src/core/hooks/<hook>.ts only for repeated hooks that are independent of page/domain context.ai/rules/client-api-hook-rules.mdRules for client API operations in apps/client/src/api.
apps/client/src/api.index.ts.src/api/client.ts so private API calls share the /api/v1 base URL, JSON handling, API error handling, and credentials: "include" behavior.useQuery or useInfiniteQuery in query hooks according to the API shape, and useMutation in mutation hooks.queryKeys.ts when hooks or mutations share them.enabled in query hooks when required IDs or inputs are unavailable.queryKeys into components.src/api/queryKeys.ts unless data is genuinely cross-domain.fetch directly outside src/api/client.ts unless the request is intentionally outside the app API contract.apps/client/src/api/<domain>/<operation>/<operation>.ts.apps/client/src/api/<domain>/<operation>/use<Operation>.ts.apps/client/src/api/<domain>/<operation>/index.ts.apps/client/src/api/<domain>/index.ts.apps/client/src/api/<domain>/queryKeys.ts.ai/architecture/client-app.mdapps/client is a Vite React application for the Capture Flag platform UI.
src/main.tsx owns top-level providers.src/router.tsx owns React Router route definitions.lazyRoute() helper and should expose named exports.src/layouts contains route layout wrappers that render shared shells, navigation, headers, and nested <Outlet /> regions.src/pages contains route-level screens. Multi-file route screens use folder modules with index.ts named exports; simple one-file screens may stay as direct page files.src/components contains shared UI used by multiple pages or sections.src/core contains context-independent client utilities and reusable hooks organized by category.src/api contains client request functions, React Query hooks, operation barrels, domain barrels, and domain query keys.src/routing contains route path and route context helpers shared by pages and layouts.src/stories contains shared Storybook fixtures and API mocks, not component stories.src/test contains shared Vitest and Testing Library helpers.PlatformLayout owns the authenticated shell, navigation frame, sidebar/header state, logout flow, and nested route outlet.useRouteContext, not stored in a mutable layout context./: redirects to /organizations./login: GitHub login screen./account: authenticated user account details and display name editing./organizations and /organizations/:organizationId: organization selection and organization members./organizations/:organizationId/projects and /organizations/:organizationId/projects/:projectId: project selection and project members./organizations/:organizationId/projects/:projectId/environments: environments for the selected project./organizations/:organizationId/projects/:projectId/configs and /organizations/:organizationId/projects/:projectId/configs/:configId: configs and public Config JSON preview./organizations/:organizationId/projects/:projectId/configs/:configId/flags: feature flags and remote config values./organizations/:organizationId/projects/:projectId/configs/:configId/segments: reusable targeting segments./organizations/:organizationId/projects/:projectId/sdk-keys: SDK key lifecycle for project configs/environments, with selected config/environment in ?configId= and ?environmentId= when needed./organizations/:organizationId/audit-logs: organization/project audit log timeline, with selected project in ?projectId= when needed.*: redirects to /.src/api/<domain>/<operation>.useQuery or useInfiniteQuery according to the API shape.useRouteContext for selected resources and redirect-safe navigation paths.src/layouts/<LayoutName> until reused by another layout or page.src/components and are imported directly through aliases such as @components/Button.components/members primitives with page-specific role options.stories/ child folder beside the source area they cover.src/pages/stories.src/stories and are reused by stories and page tests.src/core/<category>/<name>.ts.date, json, strings, validation, and hooks.src/core/date/toDate.index.ts barrels under src/core; multiple helpers require multiple explicit imports.src/core/<category>/__tests__/<name>.test.ts next to the category they cover.__tests__/ child folders beside the source area they cover, mirroring Storybook stories/ folders.src/test and provide Testing Library render helpers, React Query providers, and fetch response mocks.npm --workspace @capture-flag/client run test:coverage and should stay at 90% or higher for configured client targets.src/api/__tests__ and cover operation behavior through mocked fetch responses.src/components/__tests__; member component tests live in src/components/members/__tests__.src/pages/<PageName>/__tests__ when they cover one page folder.src/pages/<PageName>/<section>/__tests__ when they cover colocated page internals such as feature flags or segments.src/__tests__ only for source files owned directly by src, such as permissions.ts.src/components/stories; member component stories live in src/components/members/stories.src/layouts/<LayoutName>/stories.stories/ child folder.src/pages/stories and use route parameters plus shared mock data to render realistic page states.src/stories; that folder is reserved for shared Storybook data, routes, and API mocks.ai/examples/good-client-api-operation.mdSource: apps/client/src/api/projects/createProject/createProject.ts (sha256: 5510aadbf7212a2153d02de001b7211e3227050b9833b9d74f46ea522767a38e)
Source: apps/client/src/api/projects/createProject/useCreateProject.ts (sha256: 82695169e47aa65b9090e27d5acebcb91b89d5326f3bdcbffe700de16cc74af6)
Source: apps/client/src/api/projects/createProject/index.ts (sha256: 50c27c5e6cf5c4a9b979201760aa652d67d5f56785a3ea567f803cc56f3a7a4c)
Source: apps/client/src/api/auditLogs/getAuditLogs/useGetAuditLogs.ts (sha256: 5f97a43166e57aaab8312398364a5dc16754fb210fc4f2f7100a961fb6320588)
Source: apps/client/src/api/featureFlags/updateFeatureFlagEnvironmentValue/useUpdateFeatureFlagEnvironmentValue.ts (sha256: bfba73c369ba938273edf033880d189a0bea8c459cc69d445ce2ebec70cef741)
Why this is canonical:
useInfiniteQuery for cursor-paginated APIs.Canonical pattern from apps/client/src/api/projects/createProject.
export function createProject({ name, organizationId }: CreateProjectInput) {
return postJson<Project>(`/organizations/${organizationId}/projects`, { name });
}
The request function performs HTTP work only. It does not import React.
export function useCreateProject({ organizationId, onSuccess }: UseCreateProjectOptions) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (name: string) => createProject({ name, organizationId }),
onSuccess: (project) => {
void queryClient.invalidateQueries({ queryKey: projectQueryKeys.list(organizationId) });
onSuccess?.(project);
},
});
}
The mutation hook owns cache invalidation. Components consume the hook, not the request function or query keys.
export { useCreateProject } from "./useCreateProject";
Operation and domain barrels expose hooks to UI code.
export function useGetAuditLogs({ enabled = true, filters, organizationId }: UseGetAuditLogsInput) {
return useInfiniteQuery({
enabled: Boolean(enabled && organizationId),
getNextPageParam: (lastPage) => lastPage.nextCursor,
initialPageParam: null as string | null,
queryFn: ({ pageParam }) =>
getAuditLogs({
filters: {
...filters,
...(pageParam ? { cursor: pageParam } : {}),
},
organizationId,
}),
queryKey: auditLogQueryKeys.list(organizationId, filters),
});
}
Infinite query hooks still keep raw HTTP work in the request function and use serializable filter DTOs in query keys.
onSuccess: (_value, variables) => {
void queryClient.invalidateQueries({ queryKey: featureFlagQueryKeys.list(configId) });
void queryClient.invalidateQueries({
queryKey: featureFlagQueryKeys.activity(configId, variables.featureFlagId),
});
void queryClient.invalidateQueries({
queryKey: configQueryKeys.preview(configId, variables.environmentId),
});
void queryClient.invalidateQueries({ queryKey: auditLogQueryKeys.all });
};
When a mutation changes derived server state, the mutation hook imports and invalidates every affected domain query key. Components still do not import query keys.