mcpField
S
state-management

state-management

github.com/asyrafhussin开发95801 次安装9 周前更新
代码仓库

关于 state-management

面向使用 TanStack Query v5 和 Zustand v5 的 React 开发者与编码 Agent 的参考技能,涵盖服务端和客户端状态、缓存、变更及安全持久化。

主要功能

  1. 01TanStack Query v5 模式
  2. 02Zustand v5 状态仓库模式
  3. 03缓存与失效指导
  4. 04变更与乐观更新模式
  5. 05DevTools 与调试指导
  6. 06敏感状态持久化安全规范

适用场景

  1. 01获取并缓存 API 数据
  2. 02构建客户端状态仓库
  3. 03实现变更与乐观更新
  4. 04组合服务端状态与界面状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/asyrafhussin/agent-skills --skill state-management

安装后在 Claude Code 中输入/state-management确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

AGENTS.md
metadata.json
README.md
rules/
_sections.md
_template.md
rq-cache-time.md
rq-dependent-queries.md
rq-enabled-option.md
rq-infinite-queries.md
rq-initial-data.md
rq-mutation-callbacks.md
rq-mutation-setup.md
rq-mutation-side-effects.md
rq-mutation-variables.md
rq-optimistic-updates.md
rq-paginated-queries.md
rq-parallel-queries.md
rq-placeholder-data.md
rq-prefetching.md
rq-query-cancellation.md
rq-query-functions.md
rq-query-invalidation.md
rq-query-keys.md
rq-refetch-options.md
rq-retry-logic.md
rq-select-transform.md
rq-stale-time.md
rq-suspense-mode.md
rq-usequery.md
zs-create-store.md

详细介绍

使用 React Query + Zustand 进行状态管理

版本 1.1.0 | TanStack Query v5 | Zustand v5 | 2026 年 3 月

注意: 本文档为使用 TanStack Query v5 和 Zustand v5 的 AI Agent 与大模型提供全面的模式指南。所有示例均已针对 v5 API 验证。 针对自动化重构、代码生成和状态管理最佳实践进行了优化。

v5 破坏性变更(快速参考)

TanStack Query v5:

  • cacheTime → gcTime
  • keepPreviousData 选项 → placeholderData: keepPreviousData(导入的辅助函数)
  • isPreviousData → isPlaceholderData
  • useQuery 中的 onSuccess/onError/onSettled 已移除 — 在 useMutation 中仍然有效
  • useQuery 中的 suspense: true 已移除 → 改用 useSuspenseQuery

Zustand v5:

  • 已移除作为第二个参数的 shallow → 使用 zustand/shallow 中的 useShallow
  • 返回新引用的选择器需要使用 useShallow,以避免无限循环

安全性:持久化中间件

切勿将身份验证令牌、密码或机密信息持久化到 localStorage/sessionStorage。 使用 partialize 仅持久化非敏感状态。通过 HttpOnly Cookie 管理令牌。


本指南介绍服务器状态(React Query)和客户端状态(Zustand)的全面模式,包含 26 条以上关于高效数据获取和状态管理的规则。

适用场景

在以下情况下参考这些指南:

  • 从 API 获取数据
  • 管理服务器状态和缓存
  • 处理变更和乐观更新
  • 创建客户端存储
  • 将 React Query 与 Zustand 结合使用

按优先级划分的规则类别

优先级类别影响前缀
1React Query 基础知识严重rq-
2Zustand 存储模式严重zs-
3缓存与失效高cache-
4变更与更新高mut-
5乐观更新中opt-
6开发工具与调试中dev-
7高级模式低adv-

快速参考

1. React Query 基础知识(严重)
  • rq-setup - 设置 QueryClient 和 Provider
  • rq-usequery - useQuery 基本模式
  • rq-querykeys - 查询键组织
  • rq-loading-error - 处理加载和错误状态
  • rq-enabled - 条件查询
2. Zustand 存储模式(严重)
  • zs-create-store - 创建基本存储
  • zs-typescript - TypeScript 存储模式
  • zs-selectors - 高效选择器
  • zs-actions - 操作模式
  • zs-persist - 将状态持久化到存储中
3. 缓存与失效(高)
  • cache-stale-time - 配置过期时间
  • cache-gc-time - 配置垃圾回收时间
  • cache-invalidation - 使查询失效
  • cache-prefetch - 预取数据
  • cache-initial-data - 设置初始数据
4. 变更与更新(高)
  • mut-usemutation - useMutation 基础用法
  • mut-callbacks - onSuccess、onError 回调
  • mut-invalidate - 变更后使查询失效
  • mut-update-cache - 直接更新缓存
5. 乐观更新(中)
  • opt-basic - 乐观更新基础用法
  • opt-rollback - 出错时回滚
  • opt-variables - 使用变更变量
6. 开发工具与调试(中)
  • dev-react-query - React Query DevTools
  • dev-zustand - Zustand DevTools
  • dev-debugging - 调试策略
7. 高级模式(低)
  • adv-infinite-queries - 无限滚动
  • adv-parallel-queries - 并行请求
  • adv-dependent-queries - 依赖查询
  • adv-query-zustand - 将 RQ 与 Zustand 结合使用

React Query 模式

设置
// 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>
  )
}
查询键工厂
// 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,
  },
}
useQuery Hook
// 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>
  )
}
useMutation Hook
// 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>
  )
}
乐观更新
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),
      })
    },
  })
}

Zustand 模式

基本存储
// 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>
  )
}
使用 TypeScript 和中间件的存储
// 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
  isAuthenticated: boolean
  login: (user: User) => void
  logout: () => void
}

// ✅ Never persist tokens to localStorage — use HttpOnly cookies server-side
export const useAuthStore = create<AuthState>()(
  devtools(
    persist(
      (set) => ({
        user: null,
        isAuthenticated: false,

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

        logout: () =>
          set({
            user: null,
            isAuthenticated: false,
          }),
      }),
      {
        name: 'auth-storage',
        // Only persist display info and auth flag — tokens must NOT be included
        partialize: (state) => ({
          user: state.user,
          isAuthenticated: state.isAuthenticated,
        }),
      }
    )
  )
)
用于提升性能的选择器
// 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>
}
结合使用 React Query + Zustand
// 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)

使用方法

阅读各规则文件,了解详细说明和代码示例:

rules/rq-usequery.md
rules/rq-query-keys.md
rules/rq-mutation-setup.md
rules/rq-optimistic-updates.md
rules/zs-create-store.md
rules/zs-persist.md
rules/rq-query-invalidation.md
rules/rq-prefetching.md

参考资料

React Query(TanStack Query)
  1. TanStack Query 文档
  2. React Query 概览
  3. 查询指南
  4. 变更指南
  5. 查询键指南
  6. 乐观更新
  7. 无限查询
  8. 分页查询
  9. React Query 开发者工具
Zustand
  1. Zustand 演示
  2. Zustand GitHub
  3. 入门指南
  4. TypeScript 指南
  5. 持久化存储数据
  6. Zustand 示例

许可证

此技能按原样提供,仅用于教育和开发目的。React Query 由 TanStack 采用 MIT 许可证授权。Zustand 由 Poimandres(pmnd.rs)采用 MIT 许可证授权。

评论

支持 Markdown