state-management

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

State Management with React Query + Zustand

基于React Query + Zustand的状态管理

Version 1.0.0 State Management Patterns January 2026
Note: This document provides comprehensive patterns for AI agents and LLMs working with React Query (TanStack Query) and Zustand. Optimized for automated refactoring, code generation, and state management best practices.

Comprehensive patterns for server state (React Query) and client state (Zustand). Contains 26+ rules for efficient data fetching and state management.
版本 1.0.0 状态管理模式 2026年1月
注意: 本文档为AI Agent和大语言模型提供了使用React Query(TanStack Query)与Zustand的综合模式。针对自动化重构、代码生成和状态管理最佳实践进行了优化。

涵盖服务端状态(React Query)与客户端状态(Zustand)的综合模式。包含26+条高效数据获取与状态管理的规则。

When to Apply

适用场景

Reference these guidelines when:
  • Fetching data from APIs
  • Managing server state and caching
  • Handling mutations and optimistic updates
  • Creating client-side stores
  • Combining React Query with Zustand
在以下场景中参考本指南:
  • 从API获取数据
  • 管理服务端状态与缓存
  • 处理变更与乐观更新
  • 创建客户端存储
  • 结合使用React Query与Zustand

Rule Categories by Priority

按优先级划分的规则类别

PriorityCategoryImpactPrefix
1React Query BasicsCRITICAL
rq-
2Zustand Store PatternsCRITICAL
zs-
3Caching & InvalidationHIGH
cache-
4Mutations & UpdatesHIGH
mut-
5Optimistic UpdatesMEDIUM
opt-
6DevTools & DebuggingMEDIUM
dev-
7Advanced PatternsLOW
adv-
优先级类别影响程度前缀
1React Query基础关键
rq-
2Zustand存储模式关键
zs-
3缓存与失效
cache-
4变更与更新
mut-
5乐观更新
opt-
6开发工具与调试
dev-
7高级模式
adv-

Quick Reference

快速参考

1. React Query Basics (CRITICAL)

1. React Query基础(关键)

  • rq-setup
    - QueryClient and Provider setup
  • rq-usequery
    - Basic useQuery patterns
  • rq-querykeys
    - Query key organization
  • rq-loading-error
    - Handle loading and error states
  • rq-enabled
    - Conditional queries
  • rq-setup
    - QueryClient与Provider配置
  • rq-usequery
    - 基础useQuery模式
  • rq-querykeys
    - 查询键组织
  • rq-loading-error
    - 处理加载与错误状态
  • rq-enabled
    - 条件查询

2. Zustand Store Patterns (CRITICAL)

2. Zustand存储模式(关键)

  • zs-create-store
    - Create basic store
  • zs-typescript
    - TypeScript store patterns
  • zs-selectors
    - Efficient selectors
  • zs-actions
    - Action patterns
  • zs-persist
    - Persist state to storage
  • zs-create-store
    - 创建基础存储
  • zs-typescript
    - TypeScript存储模式
  • zs-selectors
    - 高效选择器
  • zs-actions
    - 操作模式
  • zs-persist
    - 将状态持久化到存储

3. Caching & Invalidation (HIGH)

3. 缓存与失效(高)

  • cache-stale-time
    - Configure stale time
  • cache-gc-time
    - Configure garbage collection
  • cache-invalidation
    - Invalidate queries
  • cache-prefetch
    - Prefetch data
  • cache-initial-data
    - Set initial data
  • cache-stale-time
    - 配置过期时间
  • cache-gc-time
    - 配置垃圾回收时间
  • cache-invalidation
    - 失效查询
  • cache-prefetch
    - 预取数据
  • cache-initial-data
    - 设置初始数据

4. Mutations & Updates (HIGH)

4. 变更与更新(高)

  • mut-usemutation
    - Basic useMutation
  • mut-callbacks
    - onSuccess, onError callbacks
  • mut-invalidate
    - Invalidate after mutation
  • mut-update-cache
    - Direct cache updates
  • mut-usemutation
    - 基础useMutation
  • mut-callbacks
    - onSuccess、onError回调
  • mut-invalidate
    - 变更后失效查询
  • mut-update-cache
    - 直接更新缓存

5. Optimistic Updates (MEDIUM)

5. 乐观更新(中)

  • opt-basic
    - Basic optimistic updates
  • opt-rollback
    - Rollback on error
  • opt-variables
    - Use mutation variables
  • opt-basic
    - 基础乐观更新
  • opt-rollback
    - 错误时回滚
  • opt-variables
    - 使用变更变量

6. DevTools & Debugging (MEDIUM)

6. 开发工具与调试(中)

  • dev-react-query
    - React Query DevTools
  • dev-zustand
    - Zustand DevTools
  • dev-debugging
    - Debug strategies
  • dev-react-query
    - React Query开发工具
  • dev-zustand
    - Zustand开发工具
  • dev-debugging
    - 调试策略

7. Advanced Patterns (LOW)

7. 高级模式(低)

  • adv-infinite-queries
    - Infinite scrolling
  • adv-parallel-queries
    - Parallel requests
  • adv-dependent-queries
    - Dependent queries
  • adv-query-zustand
    - Combine RQ with Zustand
  • adv-infinite-queries
    - 无限滚动
  • adv-parallel-queries
    - 并行请求
  • adv-dependent-queries
    - 依赖查询
  • adv-query-zustand
    - 结合React Query与Zustand

React Query Patterns

React Query模式

Setup

配置

tsx
// lib/queryClient.ts
import { QueryClient } from '@tanstack/react-query'

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 minutes
      gcTime: 1000 * 60 * 30,   // 30 minutes (formerly cacheTime)
      retry: 1,
      refetchOnWindowFocus: false,
    },
  },
})

// App.tsx
import { QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { queryClient } from './lib/queryClient'

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Router />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  )
}
tsx
// lib/queryClient.ts
import { QueryClient } from '@tanstack/react-query'

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5分钟
      gcTime: 1000 * 60 * 30,   // 30分钟(原cacheTime)
      retry: 1,
      refetchOnWindowFocus: false,
    },
  },
})

// App.tsx
import { QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { queryClient } from './lib/queryClient'

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Router />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  )
}

Query Keys Factory

查询键工厂

tsx
// lib/queryKeys.ts
export const queryKeys = {
  // All posts
  posts: {
    all: ['posts'] as const,
    lists: () => [...queryKeys.posts.all, 'list'] as const,
    list: (filters: PostFilters) =>
      [...queryKeys.posts.lists(), filters] as const,
    details: () => [...queryKeys.posts.all, 'detail'] as const,
    detail: (id: number) => [...queryKeys.posts.details(), id] as const,
  },

  // All users
  users: {
    all: ['users'] as const,
    detail: (id: number) => [...queryKeys.users.all, id] as const,
    posts: (userId: number) => [...queryKeys.users.all, userId, 'posts'] as const,
  },
}
tsx
// lib/queryKeys.ts
export const queryKeys = {
  // 所有文章
  posts: {
    all: ['posts'] as const,
    lists: () => [...queryKeys.posts.all, 'list'] as const,
    list: (filters: PostFilters) =>
      [...queryKeys.posts.lists(), filters] as const,
    details: () => [...queryKeys.posts.all, 'detail'] as const,
    detail: (id: number) => [...queryKeys.posts.details(), id] as const,
  },

  // 所有用户
  users: {
    all: ['users'] as const,
    detail: (id: number) => [...queryKeys.users.all, id] as const,
    posts: (userId: number) => [...queryKeys.users.all, userId, 'posts'] as const,
  },
}

useQuery Hook

useQuery钩子

tsx
// hooks/usePosts.ts
import { useQuery } from '@tanstack/react-query'
import { queryKeys } from '@/lib/queryKeys'
import { fetchPosts, fetchPost } from '@/api/posts'

export function usePosts(filters?: PostFilters) {
  return useQuery({
    queryKey: queryKeys.posts.list(filters ?? {}),
    queryFn: () => fetchPosts(filters),
  })
}

export function usePost(id: number) {
  return useQuery({
    queryKey: queryKeys.posts.detail(id),
    queryFn: () => fetchPost(id),
    enabled: !!id, // Only run if id exists
  })
}

// Usage in component
function PostList() {
  const { data: posts, isLoading, error } = usePosts()

  if (isLoading) return <Spinner />
  if (error) return <Error message={error.message} />

  return (
    <ul>
      {posts?.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}
tsx
// hooks/usePosts.ts
import { useQuery } from '@tanstack/react-query'
import { queryKeys } from '@/lib/queryKeys'
import { fetchPosts, fetchPost } from '@/api/posts'

export function usePosts(filters?: PostFilters) {
  return useQuery({
    queryKey: queryKeys.posts.list(filters ?? {}),
    queryFn: () => fetchPosts(filters),
  })
}

export function usePost(id: number) {
  return useQuery({
    queryKey: queryKeys.posts.detail(id),
    queryFn: () => fetchPost(id),
    enabled: !!id, // 仅当id存在时运行
  })
}

// 在组件中使用
function PostList() {
  const { data: posts, isLoading, error } = usePosts()

  if (isLoading) return <Spinner />
  if (error) return <Error message={error.message} />

  return (
    <ul>
      {posts?.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

useMutation Hook

useMutation钩子

tsx
// hooks/useCreatePost.ts
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { queryKeys } from '@/lib/queryKeys'
import { createPost } from '@/api/posts'

export function useCreatePost() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: createPost,
    onSuccess: (newPost) => {
      // Invalidate and refetch posts list
      queryClient.invalidateQueries({
        queryKey: queryKeys.posts.lists(),
      })
    },
    onError: (error) => {
      console.error('Failed to create post:', error)
    },
  })
}

// Usage
function CreatePostForm() {
  const { mutate, isPending } = useCreatePost()

  const handleSubmit = (data: CreatePostData) => {
    mutate(data)
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* form fields */}
      <button disabled={isPending}>
        {isPending ? 'Creating...' : 'Create'}
      </button>
    </form>
  )
}
tsx
// hooks/useCreatePost.ts
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { queryKeys } from '@/lib/queryKeys'
import { createPost } from '@/api/posts'

export function useCreatePost() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: createPost,
    onSuccess: (newPost) => {
      // 失效并重新获取文章列表
      queryClient.invalidateQueries({
        queryKey: queryKeys.posts.lists(),
      })
    },
    onError: (error) => {
      console.error('创建文章失败:', error)
    },
  })
}

// 使用示例
function CreatePostForm() {
  const { mutate, isPending } = useCreatePost()

  const handleSubmit = (data: CreatePostData) => {
    mutate(data)
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* 表单字段 */}
      <button disabled={isPending}>
        {isPending ? '创建中...' : '创建'}
      </button>
    </form>
  )
}

Optimistic Updates

乐观更新

tsx
export function useUpdatePost() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: updatePost,
    onMutate: async (updatedPost) => {
      // Cancel outgoing refetches
      await queryClient.cancelQueries({
        queryKey: queryKeys.posts.detail(updatedPost.id),
      })

      // Snapshot previous value
      const previousPost = queryClient.getQueryData(
        queryKeys.posts.detail(updatedPost.id)
      )

      // Optimistically update
      queryClient.setQueryData(
        queryKeys.posts.detail(updatedPost.id),
        updatedPost
      )

      return { previousPost }
    },
    onError: (err, updatedPost, context) => {
      // Rollback on error
      queryClient.setQueryData(
        queryKeys.posts.detail(updatedPost.id),
        context?.previousPost
      )
    },
    onSettled: (data, error, variables) => {
      // Refetch after settle
      queryClient.invalidateQueries({
        queryKey: queryKeys.posts.detail(variables.id),
      })
    },
  })
}
tsx
export function useUpdatePost() {
  const queryClient = useQueryClient()

  return useMutation({
    mutationFn: updatePost,
    onMutate: async (updatedPost) => {
      // 取消正在进行的重新获取
      await queryClient.cancelQueries({
        queryKey: queryKeys.posts.detail(updatedPost.id),
      })

      // 快照之前的值
      const previousPost = queryClient.getQueryData(
        queryKeys.posts.detail(updatedPost.id)
      )

      // 乐观更新
      queryClient.setQueryData(
        queryKeys.posts.detail(updatedPost.id),
        updatedPost
      )

      return { previousPost }
    },
    onError: (err, updatedPost, context) => {
      // 错误时回滚
      queryClient.setQueryData(
        queryKeys.posts.detail(updatedPost.id),
        context?.previousPost
      )
    },
    onSettled: (data, error, variables) => {
      // 完成后重新获取
      queryClient.invalidateQueries({
        queryKey: queryKeys.posts.detail(variables.id),
      })
    },
  })
}

Zustand Patterns

Zustand模式

Basic Store

基础存储

tsx
// stores/useCounterStore.ts
import { create } from 'zustand'

interface CounterState {
  count: number
  increment: () => void
  decrement: () => void
  reset: () => void
}

export const useCounterStore = create<CounterState>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}))

// Usage
function Counter() {
  const { count, increment, decrement } = useCounterStore()

  return (
    <div>
      <span>{count}</span>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  )
}
tsx
// stores/useCounterStore.ts
import { create } from 'zustand'

interface CounterState {
  count: number
  increment: () => void
  decrement: () => void
  reset: () => void
}

export const useCounterStore = create<CounterState>((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}))

// 使用示例
function Counter() {
  const { count, increment, decrement } = useCounterStore()

  return (
    <div>
      <span>{count}</span>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  )
}

Store with TypeScript and Middleware

结合TypeScript与中间件的存储

tsx
// stores/useAuthStore.ts
import { create } from 'zustand'
import { persist, devtools } from 'zustand/middleware'

interface User {
  id: number
  name: string
  email: string
}

interface AuthState {
  user: User | null
  token: string | null
  isAuthenticated: boolean
  login: (user: User, token: string) => void
  logout: () => void
}

export const useAuthStore = create<AuthState>()(
  devtools(
    persist(
      (set) => ({
        user: null,
        token: null,
        isAuthenticated: false,

        login: (user, token) =>
          set({
            user,
            token,
            isAuthenticated: true,
          }),

        logout: () =>
          set({
            user: null,
            token: null,
            isAuthenticated: false,
          }),
      }),
      {
        name: 'auth-storage',
        partialize: (state) => ({
          user: state.user,
          token: state.token,
          isAuthenticated: state.isAuthenticated,
        }),
      }
    )
  )
)
tsx
// stores/useAuthStore.ts
import { create } from 'zustand'
import { persist, devtools } from 'zustand/middleware'

interface User {
  id: number
  name: string
  email: string
}

interface AuthState {
  user: User | null
  token: string | null
  isAuthenticated: boolean
  login: (user: User, token: string) => void
  logout: () => void
}

export const useAuthStore = create<AuthState>()(
  devtools(
    persist(
      (set) => ({
        user: null,
        token: null,
        isAuthenticated: false,

        login: (user, token) =>
          set({
            user,
            token,
            isAuthenticated: true,
          }),

        logout: () =>
          set({
            user: null,
            token: null,
            isAuthenticated: false,
          }),
      }),
      {
        name: 'auth-storage',
        partialize: (state) => ({
          user: state.user,
          token: state.token,
          isAuthenticated: state.isAuthenticated,
        }),
      }
    )
  )
)

Selectors for Performance

性能优化选择器

tsx
// Use selectors to prevent unnecessary re-renders
function UserName() {
  // Only re-renders when user.name changes
  const name = useAuthStore((state) => state.user?.name)
  return <span>{name}</span>
}

// Multiple selectors
function UserInfo() {
  const user = useAuthStore((state) => state.user)
  const isAuthenticated = useAuthStore((state) => state.isAuthenticated)

  if (!isAuthenticated) return <LoginButton />
  return <span>{user?.name}</span>
}
tsx
// 使用选择器避免不必要的重渲染
function UserName() {
  // 仅当user.name变化时重渲染
  const name = useAuthStore((state) => state.user?.name)
  return <span>{name}</span>
}

// 多选择器示例
function UserInfo() {
  const user = useAuthStore((state) => state.user)
  const isAuthenticated = useAuthStore((state) => state.isAuthenticated)

  if (!isAuthenticated) return <LoginButton />
  return <span>{user?.name}</span>
}

Combining React Query + Zustand

结合React Query + Zustand

tsx
// Server state: React Query (what comes from API)
const { data: posts } = usePosts()

// Client state: Zustand (UI state)
const { selectedPostId, selectPost } = useUIStore()

// Use together
const selectedPost = posts?.find((p) => p.id === selectedPostId)
tsx
// 服务端状态:React Query(来自API的数据)
const { data: posts } = usePosts()

// 客户端状态:Zustand(UI状态)
const { selectedPostId, selectPost } = useUIStore()

// 结合使用
const selectedPost = posts?.find((p) => p.id === selectedPostId)

How to Use

使用方法

Read individual rule files for detailed explanations and code examples:
rules/rq-usequery.md
rules/zs-create-store.md
rules/cache-invalidation.md

阅读单个规则文件获取详细说明与代码示例:
rules/rq-usequery.md
rules/zs-create-store.md
rules/cache-invalidation.md

References

参考资料

React Query (TanStack Query)

React Query (TanStack Query)

Zustand

Zustand

License

许可证

This skill is provided as-is for educational and development purposes. React Query is MIT licensed by TanStack. Zustand is MIT licensed by Poimandres (pmnd.rs).
本技能仅供教育与开发使用。React Query由TanStack以MIT许可证发布。Zustand由Poimandres(pmnd.rs)以MIT许可证发布。