state-management
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseResources
资源
scripts/
validate-state.sh
references/
state-patterns.mdscripts/
validate-state.sh
references/
state-patterns.mdState Management
状态管理
This skill guides you through state architecture decisions and implementation using GoodVibes precision tools. Use this workflow when choosing state solutions, implementing data fetching patterns, or managing complex form state.
本技能将引导你使用GoodVibes精准工具完成状态架构决策与实现。当你需要选择状态管理方案、实现数据获取模式或管理复杂表单状态时,可使用本工作流。
When to Use This Skill
何时使用本技能
Load this skill when:
- Deciding between state management approaches
- Implementing server data fetching with caching
- Building forms with complex validation
- Managing client-side application state
- Implementing URL-based state patterns
- Migrating from one state solution to another
Trigger phrases: "state management", "TanStack Query", "Zustand", "React Hook Form", "form validation", "data fetching", "cache invalidation", "URL state".
在以下场景加载本技能:
- 选择状态管理方案时
- 实现带缓存的服务端数据获取时
- 构建带复杂验证的表单时
- 管理客户端应用状态时
- 实现基于URL的状态模式时
- 从一种状态管理方案迁移到另一种时
触发短语:「state management」「TanStack Query」「Zustand」「React Hook Form」「form validation」「data fetching」「cache invalidation」「URL state」。
Core Workflow
核心工作流
Phase 1: Discovery
第一阶段:发现调研
Before choosing a state solution, understand existing patterns.
在选择状态管理方案前,先了解现有模式。
Step 1.1: Identify Current State Libraries
步骤1.1:识别当前使用的状态管理库
Use to find existing state management solutions.
discoveryaml
discover:
queries:
- id: state_libraries
type: grep
pattern: "(from 'zustand'|from '@tanstack/react-query'|from 'react-hook-form'|from 'jotai'|from 'redux'|useContext)"
glob: "**/*.{ts,tsx}"
- id: data_fetching
type: grep
pattern: "(useQuery|useMutation|useSWR|fetch|axios)"
glob: "**/*.{ts,tsx}"
- id: form_libraries
type: grep
pattern: "(useForm|Formik|react-hook-form)"
glob: "**/*.{ts,tsx}"
verbosity: files_onlyWhat this reveals:
- Existing state management libraries in use
- Data fetching patterns (REST, GraphQL, etc.)
- Form handling approaches
- Consistency across the codebase
使用工具查找现有的状态管理解决方案。
discoveryaml
discover:
queries:
- id: state_libraries
type: grep
pattern: "(from 'zustand'|from '@tanstack/react-query'|from 'react-hook-form'|from 'jotai'|from 'redux'|useContext)"
glob: "**/*.{ts,tsx}"
- id: data_fetching
type: grep
pattern: "(useQuery|useMutation|useSWR|fetch|axios)"
glob: "**/*.{ts,tsx}"
- id: form_libraries
type: grep
pattern: "(useForm|Formik|react-hook-form)"
glob: "**/*.{ts,tsx}"
verbosity: files_only该操作可揭示:
- 当前项目中使用的状态管理库
- 数据获取模式(REST、GraphQL等)
- 表单处理方式
- 代码库的一致性
Step 1.2: Analyze Package Dependencies
步骤1.2:分析包依赖
Use to check what's installed.
precision_readyaml
precision_read:
files:
- path: "package.json"
extract: content
verbosity: minimalLook for:
- (server state)
@tanstack/react-query - (client state)
zustand - +
react-hook-form(forms)zod - or similar (URL state)
nuqs
使用工具检查已安装的依赖。
precision_readyaml
precision_read:
files:
- path: "package.json"
extract: content
verbosity: minimal重点查找:
- (服务端状态)
@tanstack/react-query - (客户端状态)
zustand - +
react-hook-form(表单)zod - 或类似工具(URL状态)
nuqs
Step 1.3: Read Existing State Patterns
步骤1.3:阅读现有状态实现模式
Read 2-3 examples to understand implementation patterns.
yaml
precision_read:
files:
- path: "src/lib/query-client.ts" # or discovered file
extract: content
- path: "src/stores/user-store.ts" # or discovered file
extract: content
verbosity: standard阅读2-3个示例文件,了解现有实现模式。
yaml
precision_read:
files:
- path: "src/lib/query-client.ts" # 或已发现的对应文件
extract: content
- path: "src/stores/user-store.ts" # 或已发现的对应文件
extract: content
verbosity: standardPhase 2: State Categorization
第二阶段:状态分类
Choose the right tool for each type of state. See for the complete decision tree.
references/state-patterns.md为每种类型的状态选择合适的工具。完整决策树请参考。
references/state-patterns.mdQuick Decision Guide
快速决策指南
| State Type | Best Tool | Use When |
|---|---|---|
| Server state | TanStack Query | Data from APIs, needs caching/invalidation |
| Client state | Zustand | UI state shared across components |
| Form state | React Hook Form + Zod | Complex forms with validation |
| URL state | nuqs or searchParams | Sharable, bookmarkable state |
| Component state | useState | Local to one component |
State Colocation Principle:
Keep state as close to where it's used as possible. Start with , lift to parent when shared, then consider dedicated solutions only when necessary.
useState| 状态类型 | 最佳工具 | 使用场景 |
|---|---|---|
| 服务端状态 | TanStack Query | API数据,需要缓存/失效机制 |
| 客户端状态 | Zustand | 组件间共享的UI状态 |
| 表单状态 | React Hook Form + Zod | 带复杂验证的表单 |
| URL状态 | nuqs 或 searchParams | 可分享、可收藏的状态 |
| 组件内部状态 | useState | 仅单个组件使用的本地状态 |
状态就近原则:
将状态尽可能放在离使用它的位置最近的地方。优先使用,需要共享时提升到父组件,仅在必要时再考虑专用的状态管理方案。
useStatePhase 3: Server State with TanStack Query
第三阶段:使用TanStack Query管理服务端状态
For data from APIs that needs caching, background updates, and optimistic mutations.
适用于需要缓存、后台更新和乐观更新的API数据。
Step 3.1: Install Dependencies
步骤3.1:安装依赖
Check if installed, otherwise add:
bash
npm install @tanstack/react-query # Note: Targeting TanStack Query v5
npm install -D @tanstack/react-query-devtools检查是否已安装,若未安装则执行以下命令:
bash
npm install @tanstack/react-query # 注意:目标版本为TanStack Query v5
npm install -D @tanstack/react-query-devtoolsStep 3.2: Set Up Query Client
步骤3.2:配置Query Client
Create a query client configuration.
typescript
// src/lib/query-client.ts
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60, // 1 minute
retry: 1,
refetchOnWindowFocus: false,
},
},
});创建Query Client配置文件。
typescript
// src/lib/query-client.ts
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60, // 1分钟
retry: 1,
refetchOnWindowFocus: false,
},
},
});Step 3.3: Implement Query Patterns
步骤3.3:实现查询模式
Basic Query:
typescript
import { useQuery } from '@tanstack/react-query';
import { getUser } from '@/lib/api';
export function useUser(userId: string) {
return useQuery({
queryKey: ['user', userId],
queryFn: () => getUser(userId),
enabled: !!userId, // Don't run if no userId
});
}Mutation with Optimistic Update:
typescript
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { updateUser } from '@/lib/api';
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
// Cancel outgoing queries
await queryClient.cancelQueries({ queryKey: ['user', newUser.id] });
// Snapshot previous value
const previousUser = queryClient.getQueryData(['user', newUser.id]);
// Optimistically update
queryClient.setQueryData(['user', newUser.id], newUser);
return { previousUser };
},
onError: (err, newUser, context) => {
// Rollback on error
queryClient.setQueryData(
['user', newUser.id],
context?.previousUser
);
},
onSettled: (data, error, variables) => {
// Refetch after error or success
queryClient.invalidateQueries({ queryKey: ['user', variables.id] });
},
});
}Cache Invalidation:
typescript
// Invalidate all user queries
queryClient.invalidateQueries({ queryKey: ['user'] });
// Invalidate specific user
queryClient.invalidateQueries({ queryKey: ['user', userId] });
// Remove from cache entirely
queryClient.removeQueries({ queryKey: ['user', userId] });Consuming Error and Loading States:
typescript
function UserProfile({ userId }: { userId: string }) {
const { data, isPending, isError, error } = useUser(userId);
if (isPending) return <Skeleton />;
if (isError) return <ErrorDisplay error={error} />;
return <UserProfile user={data} />;
}基础查询:
typescript
import { useQuery } from '@tanstack/react-query';
import { getUser } from '@/lib/api';
export function useUser(userId: string) {
return useQuery({
queryKey: ['user', userId],
queryFn: () => getUser(userId),
enabled: !!userId, // 无userId时不执行
});
}带乐观更新的Mutation:
typescript
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { updateUser } from '@/lib/api';
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
// 取消正在进行的相关查询
await queryClient.cancelQueries({ queryKey: ['user', newUser.id] });
// 记录之前的数值
const previousUser = queryClient.getQueryData(['user', newUser.id]);
// 执行乐观更新
queryClient.setQueryData(['user', newUser.id], newUser);
return { previousUser };
},
onError: (err, newUser, context) => {
// 出错时回滚
queryClient.setQueryData(
['user', newUser.id],
context?.previousUser
);
},
onSettled: (data, error, variables) => {
// 无论成功或失败,重新获取数据
queryClient.invalidateQueries({ queryKey: ['user', variables.id] });
},
});
}缓存失效:
typescript
// 使所有用户相关查询失效
queryClient.invalidateQueries({ queryKey: ['user'] });
// 使特定用户的查询失效
queryClient.invalidateQueries({ queryKey: ['user', userId] });
// 从缓存中彻底移除
queryClient.removeQueries({ queryKey: ['user', userId] });处理错误与加载状态:
typescript
function UserProfile({ userId }: { userId: string }) {
const { data, isPending, isError, error } = useUser(userId);
if (isPending) return <Skeleton />;
if (isError) return <ErrorDisplay error={error} />;
return <UserProfile user={data} />;
}Phase 4: Client State with Zustand
第四阶段:使用Zustand管理客户端状态
For UI state shared across components (modals, themes, filters).
适用于组件间共享的UI状态(如模态框、主题、筛选条件)。
Step 4.1: Install Dependencies
步骤4.1:安装依赖
bash
npm install zustandbash
npm install zustandStep 4.2: Create Store
步骤4.2:创建状态仓库
Simple Store:
typescript
import { create } from 'zustand';
interface UIStore {
sidebarOpen: boolean;
toggleSidebar: () => void;
theme: 'light' | 'dark';
setTheme: (theme: 'light' | 'dark') => void;
}
export const useUIStore = create<UIStore>((set) => ({
sidebarOpen: true,
toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
theme: 'light',
setTheme: (theme) => set({ theme }),
}));Store with Slices:
typescript
import { create, StateCreator } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
// Slice pattern for organization
interface AuthSlice {
user: User | null;
setUser: (user: User | null) => void;
}
interface UISlice {
sidebarOpen: boolean;
toggleSidebar: () => void;
}
type Store = AuthSlice & UISlice;
const createAuthSlice: StateCreator<Store, [], [], AuthSlice> = (set) => ({
user: null,
setUser: (user) => set({ user }),
});
const createUISlice: StateCreator<Store, [], [], UISlice> = (set) => ({
sidebarOpen: true,
toggleSidebar: () => set((state: Store) => ({
sidebarOpen: !state.sidebarOpen
})),
});
export const useStore = create<Store>()(
devtools(
persist(
(...a) => ({
...createAuthSlice(...a),
...createUISlice(...a),
}),
{ name: 'app-store' }
)
)
);Using Selectors:
typescript
// Avoid re-renders by selecting only what you need
const sidebarOpen = useUIStore((state) => state.sidebarOpen);
const toggleSidebar = useUIStore((state) => state.toggleSidebar);简单仓库:
typescript
import { create } from 'zustand';
interface UIStore {
sidebarOpen: boolean;
toggleSidebar: () => void;
theme: 'light' | 'dark';
setTheme: (theme: 'light' | 'dark') => void;
}
export const useUIStore = create<UIStore>((set) => ({
sidebarOpen: true,
toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),
theme: 'light',
setTheme: (theme) => set({ theme }),
}));分片式仓库:
typescript
import { create, StateCreator } from 'zustand';
import { devtools, persist } from 'zustand/middleware';
// 分片模式用于组织代码
interface AuthSlice {
user: User | null;
setUser: (user: User | null) => void;
}
interface UISlice {
sidebarOpen: boolean;
toggleSidebar: () => void;
}
type Store = AuthSlice & UISlice;
const createAuthSlice: StateCreator<Store, [], [], AuthSlice> = (set) => ({
user: null,
setUser: (user) => set({ user }),
});
const createUISlice: StateCreator<Store, [], [], UISlice> = (set) => ({
sidebarOpen: true,
toggleSidebar: () => set((state: Store) => ({
sidebarOpen: !state.sidebarOpen
})),
});
export const useStore = create<Store>()(
devtools(
persist(
(...a) => ({
...createAuthSlice(...a),
...createUISlice(...a),
}),
{ name: 'app-store' }
)
)
);使用选择器:
typescript
// 通过选择器仅获取需要的状态,避免不必要的重渲染
const sidebarOpen = useUIStore((state) => state.sidebarOpen);
const toggleSidebar = useUIStore((state) => state.toggleSidebar);Phase 5: Form State with React Hook Form + Zod
第五阶段:使用React Hook Form + Zod管理表单状态
For complex forms with validation, field arrays, and nested objects.
适用于带复杂验证、字段数组和嵌套对象的表单。
Step 5.1: Install Dependencies
步骤5.1:安装依赖
bash
npm install react-hook-form zod @hookform/resolversbash
npm install react-hook-form zod @hookform/resolversStep 5.2: Define Validation Schema
步骤5.2:定义验证Schema
typescript
import { z } from 'zod';
export const userSchema = z.object({
email: z.string().email('Invalid email address'),
name: z.string().min(2, 'Name must be at least 2 characters'),
age: z.number().min(18, 'Must be 18 or older'),
role: z.enum(['user', 'admin']).default('user'),
preferences: z.object({
newsletter: z.boolean().default(false),
notifications: z.boolean().default(true),
}),
});
export type UserFormData = z.infer<typeof userSchema>;typescript
import { z } from 'zod';
export const userSchema = z.object({
email: z.string().email('无效的邮箱地址'),
name: z.string().min(2, '姓名至少需要2个字符'),
age: z.number().min(18, '必须年满18岁'),
role: z.enum(['user', 'admin']).default('user'),
preferences: z.object({
newsletter: z.boolean().default(false),
notifications: z.boolean().default(true),
}),
});
export type UserFormData = z.infer<typeof userSchema>;Step 5.3: Implement Form
步骤5.3:实现表单
Basic Form:
typescript
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { userSchema, type UserFormData } from './schema';
export function UserForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<UserFormData>({
resolver: zodResolver(userSchema),
defaultValues: {
role: 'user',
preferences: {
newsletter: false,
notifications: true,
},
},
});
const onSubmit = async (data: UserFormData) => {
await createUser(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
<input {...register('name')} />
{errors.name && <span>{errors.name.message}</span>}
<input type="number" {...register('age', { valueAsNumber: true })} />
{errors.age && <span>{errors.age.message}</span>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}Field Arrays:
typescript
import { useFieldArray } from 'react-hook-form';
const schema = z.object({
users: z.array(
z.object({
name: z.string().min(1),
email: z.string().email(),
})
).min(1, 'At least one user required'),
});
function UsersForm() {
const { control, register } = useForm({
resolver: zodResolver(schema),
});
const { fields, append, remove } = useFieldArray({
control,
name: 'users',
});
return (
<div>
{fields.map((field, index) => (
<div key={field.id}>
<input {...register(`users.${index}.name`)} />
<input {...register(`users.${index}.email`)} />
<button type="button" onClick={() => remove(index)}>
Remove
</button>
</div>
))}
<button
type="button"
onClick={() => append({ name: '', email: '' })}
>
Add User
</button>
</div>
);
}基础表单:
typescript
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { userSchema, type UserFormData } from './schema';
export function UserForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<UserFormData>({
resolver: zodResolver(userSchema),
defaultValues: {
role: 'user',
preferences: {
newsletter: false,
notifications: true,
},
},
});
const onSubmit = async (data: UserFormData) => {
await createUser(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
<input {...register('name')} />
{errors.name && <span>{errors.name.message}</span>}
<input type="number" {...register('age', { valueAsNumber: true })} />
{errors.age && <span>{errors.age.message}</span>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? '提交中...' : '提交'}
</button>
</form>
);
}字段数组:
typescript
import { useFieldArray } from 'react-hook-form';
const schema = z.object({
users: z.array(
z.object({
name: z.string().min(1),
email: z.string().email(),
})
).min(1, '至少需要添加一个用户'),
});
function UsersForm() {
const { control, register } = useForm({
resolver: zodResolver(schema),
});
const { fields, append, remove } = useFieldArray({
control,
name: 'users',
});
return (
<div>
{fields.map((field, index) => (
<div key={field.id}>
<input {...register(`users.${index}.name`)} />
<input {...register(`users.${index}.email`)} />
<button type="button" onClick={() => remove(index)}>
删除
</button>
</div>
))}
<button
type="button"
onClick={() => append({ name: '', email: '' })}
>
添加用户
</button>
</div>
);
}Phase 6: URL State Patterns
第六阶段:URL状态模式
For state that should be shareable and bookmarkable.
适用于需要分享和收藏的状态。
Step 6.1: Using nuqs (Recommended)
步骤6.1:使用nuqs(推荐)
bash
npm install nuqs # Note: Targeting nuqs v1.xtypescript
import { useQueryState, parseAsInteger, parseAsStringEnum } from 'nuqs';
export function ProductList() {
const [page, setPage] = useQueryState(
'page',
parseAsInteger.withDefault(1)
);
const [sort, setSort] = useQueryState(
'sort',
parseAsStringEnum(['name', 'price', 'date']).withDefault('name')
);
// URL: /products?page=2&sort=price
// Automatically synced, type-safe, bookmarkable
}bash
npm install nuqs # 注意:目标版本为nuqs v1.xtypescript
import { useQueryState, parseAsInteger, parseAsStringEnum } from 'nuqs';
export function ProductList() {
const [page, setPage] = useQueryState(
'page',
parseAsInteger.withDefault(1)
);
const [sort, setSort] = useQueryState(
'sort',
parseAsStringEnum(['name', 'price', 'date']).withDefault('name')
);
// URL示例:/products?page=2&sort=price
// 自动同步、类型安全、可收藏
}Step 6.2: Using Next.js searchParams
步骤6.2:使用Next.js的searchParams
typescript
import { useSearchParams, useRouter } from 'next/navigation';
export function ProductList() {
const searchParams = useSearchParams();
const router = useRouter();
const page = Number(searchParams.get('page')) || 1;
const setPage = (newPage: number) => {
const params = new URLSearchParams(searchParams);
params.set('page', String(newPage));
router.push(`?${params.toString()}`);
};
}typescript
import { useSearchParams, useRouter } from 'next/navigation';
export function ProductList() {
const searchParams = useSearchParams();
const router = useRouter();
const page = Number(searchParams.get('page')) || 1;
const setPage = (newPage: number) => {
const params = new URLSearchParams(searchParams);
params.set('page', String(newPage));
router.push(`?${params.toString()}`);
};
}Phase 7: Implementation with Precision Tools
第七阶段:使用精准工具实现
Use to create state management files.
precision_writeyaml
precision_write:
files:
- path: "src/lib/query-client.ts"
content: |
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({ ... });
- path: "src/stores/ui-store.ts"
content: |
import { create } from 'zustand';
export const useUIStore = create({ ... });
- path: "src/schemas/user-schema.ts"
content: |
import { z } from 'zod';
export const userSchema = z.object({ ... });
verbosity: count_only使用工具创建状态管理相关文件。
precision_writeyaml
precision_write:
files:
- path: "src/lib/query-client.ts"
content: |
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({ ... });
- path: "src/stores/ui-store.ts"
content: |
import { create } from 'zustand';
export const useUIStore = create({ ... });
- path: "src/schemas/user-schema.ts"
content: |
import { z } from 'zod';
export const userSchema = z.object({ ... });
verbosity: count_onlyPhase 8: Validation
第八阶段:验证
Step 8.1: Run Validation Script
步骤8.1:运行验证脚本
Use the validation script to ensure quality.
bash
bash scripts/validate-state.sh .See for the complete validation suite.
scripts/validate-state.sh使用验证脚本确保代码质量。
bash
bash scripts/validate-state.sh .完整验证套件请查看。
scripts/validate-state.shStep 8.2: Type Check
步骤8.2:类型检查
Verify TypeScript compilation.
yaml
precision_exec:
commands:
- cmd: "npm run typecheck"
expect:
exit_code: 0
verbosity: minimal验证TypeScript编译是否通过。
yaml
precision_exec:
commands:
- cmd: "npm run typecheck"
expect:
exit_code: 0
verbosity: minimalCommon Patterns
常见模式
Pattern 1: Combine TanStack Query + Zustand
模式1:结合TanStack Query + Zustand
typescript
// Server data with TanStack Query
const { data: user } = useQuery({ queryKey: ['user'], queryFn: getUser });
// UI state with Zustand
const { sidebarOpen, toggleSidebar } = useUIStore();typescript
// 使用TanStack Query获取服务端数据
const { data: user } = useQuery({ queryKey: ['user'], queryFn: getUser });
// 使用Zustand管理UI状态
const { sidebarOpen, toggleSidebar } = useUIStore();Pattern 2: Form with Server Mutation
模式2:表单结合服务端Mutation
typescript
const mutation = useMutation({
mutationFn: createUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
const onSubmit = (data: UserFormData) => {
mutation.mutate(data);
};typescript
const mutation = useMutation({
mutationFn: createUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
const onSubmit = (data: UserFormData) => {
mutation.mutate(data);
};Pattern 3: URL State Driving Data Fetching
模式3:URL状态驱动数据获取
typescript
const [page] = useQueryState('page', parseAsInteger.withDefault(1));
const [search] = useQueryState('search');
const { data } = useQuery({
queryKey: ['products', page, search],
queryFn: () => getProducts({ page, search }),
});typescript
const [page] = useQueryState('page', parseAsInteger.withDefault(1));
const [search] = useQueryState('search');
const { data } = useQuery({
queryKey: ['products', page, search],
queryFn: () => getProducts({ page, search }),
});Common Anti-Patterns
常见反模式
DON'T:
- Use global state for server data (use TanStack Query)
- Put everything in Zustand (colocate when possible)
- Manage form state manually (use React Hook Form)
- Store UI state in URL params (use Zustand)
- Use Context for frequently changing data (causes re-renders)
- Forget to invalidate cache after mutations
- Skip validation schemas for forms
- Use types in state stores
any
DO:
- Match state type to the right tool
- Colocate state when possible
- Use selectors to prevent re-renders
- Implement optimistic updates for better UX
- Validate all form inputs with Zod
- Use TypeScript for all state definitions
- Invalidate queries after mutations
- Keep query keys consistent
请勿:
- 使用全局状态管理服务端数据(应使用TanStack Query)
- 将所有状态都放入Zustand(尽可能就近存放)
- 手动管理表单状态(应使用React Hook Form)
- 将UI状态存储在URL参数中(应使用Zustand)
- 使用Context管理频繁变化的数据(会导致不必要的重渲染)
- 执行Mutation后忘记失效缓存
- 不为表单设置验证Schema
- 在状态仓库中使用类型
any
建议:
- 为不同类型的状态选择合适的工具
- 尽可能就近存放状态
- 使用选择器避免不必要的重渲染
- 实现乐观更新以提升用户体验
- 使用Zod验证所有表单输入
- 为所有状态定义使用TypeScript
- 执行Mutation后失效相关查询
- 保持查询键的一致性
Quick Reference
快速参考
Discovery Phase:
yaml
discover: { queries: [state_libraries, data_fetching, forms], verbosity: files_only }
precision_read: { files: ["package.json", example stores], extract: content }Implementation Phase:
yaml
precision_write: { files: [query-client, stores, schemas], verbosity: count_only }Validation Phase:
yaml
precision_exec: { commands: [{ cmd: "npm run typecheck" }], verbosity: minimal }Post-Implementation:
bash
bash scripts/validate-state.sh .For detailed patterns and decision trees, see .
references/state-patterns.md发现调研阶段:
yaml
discover: { queries: [state_libraries, data_fetching, forms], verbosity: files_only }
precision_read: { files: ["package.json", 示例状态仓库文件], extract: content }实现阶段:
yaml
precision_write: { files: [query-client, 状态仓库, 验证Schema], verbosity: count_only }验证阶段:
yaml
precision_exec: { commands: [{ cmd: "npm run typecheck" }], verbosity: minimal }实现后:
bash
bash scripts/validate-state.sh .详细模式和决策树请参考。
references/state-patterns.md