name: state-management
description: TanStack Query + Zustand 模式。
状态管理
核心理念
- 服务器状态 → TanStack Query
- 客户端状态 → Zustand
- 表单状态 → React Hook Form + Zod
- URL 状态 → nuqs 或 searchParams
TanStack Query
查询键工厂模式
export const userKeys = {
all: ['users'] as const,
lists: () => [...userKeys.all, 'list'] as const,
list: (filters: Filters) => [...userKeys.lists(), filters] as const,
details: () => [...userKeys.all, 'detail'] as const,
detail: (id: string) => [...userKeys.details(), id] as const,
};
钩子模式
export function useUsers(filters?: Filters) {
return useQuery({
queryKey: userKeys.list(filters ?? {}),
queryFn: () => getUsers(filters),
});
}
export function useUser(id: string) {
return useQuery({
queryKey: userKeys.detail(id),
queryFn: () => getUser(id),
enabled: !!id,
});
}
export function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createUser,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
},
});
}
export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ id, data }: { id: string; data: UpdateUserInput }) =>
updateUser(id, data),
onSuccess: (_, { id }) => {
queryClient.invalidateQueries({ queryKey: userKeys.detail(id) });
queryClient.invalidateQueries({ queryKey: userKeys.lists() });
},
});
}
Zustand
类型化状态存储
interface UIStore {
sidebarOpen: boolean;
theme: 'light' | 'dark';
toggleSidebar: () => void;
setTheme: (theme: 'light' | 'dark') => void;
}
export const useUIStore = create<UIStore>((set) => ({
sidebarOpen: true,
theme: 'light',
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
setTheme: (theme) => set({ theme }),
}));
性能优化:使用选择器
// 正确 - 仅在 sidebarOpen 变化时重新渲染
const sidebarOpen = useUIStore((s) => s.sidebarOpen);
// 错误 - 任何状态变化都会导致重新渲染
const { sidebarOpen } = useUIStore();
持久化中间件
import { persist } from 'zustand/middleware';
export const useSettingsStore = create<SettingsStore>()(
persist(
(set) => ({
language: 'en',
setLanguage: (language) => set({ language }),
}),
{
name: 'settings-storage',
}
)
);
使用选择器计算派生值
// 创建一个选择器
const selectFilteredItems = (state: Store) =>
state.items.filter(item => item.active);
// 在组件中使用
const filteredItems = useStore(selectFilteredItems);
表单状态:React Hook Form + Zod
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(1, '必填项'),
email: z.string().email('邮箱格式无效'),
});
type FormData = z.infer<typeof schema>;
export function UserForm() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = (data: FormData) => {
// data 是经过类型检查和验证的
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('name')} />
{errors.name && <span>{errors.name.message}</span>}
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
<button type="submit">提交</button>
</form>
);
}