TanStack Query(React Query) 常用api及操作总结

目录

一、常用API

[核心 Hook](#核心 Hook)

查询相关

修改相关

缓存管理

全局配置(QueryClient)

开发工具

[其他实用 API](#其他实用 API)

查询状态辅助

乐观更新

常用的查询键模式

二、常用操作

[🔄 数据获取与查询](#🔄 数据获取与查询)

基础查询

分页查询

[✏️ 数据修改与提交](#✏️ 数据修改与提交)

基本提交

乐观更新(推荐👍)

批量操作

[📊 缓存管理](#📊 缓存管理)

主动更新缓存

预加载数据

[🎯 查询优化技巧](#🎯 查询优化技巧)

防抖/节流查询

依赖查询优化

[🔧 常用配置模式](#🔧 常用配置模式)

全局默认配置

请求取消

[三、🚀 实战示例](#三、🚀 实战示例)

[表单提交 + 缓存更新](#表单提交 + 缓存更新)

[分页 + 保持数据](#分页 + 保持数据)

[四、📝 实践总结](#四、📝 实践总结)


继上篇基础使用介绍:https://blog.csdn.net/qq_50909707/article/details/155780708?spm=1001.2014.3001.5501

本篇文章总结react query的常用api及操作。

一、常用API

React Query 主要 API 可以分为以下几类:

核心 Hook

查询相关
javascript 复制代码
import { useQuery, useQueries, useInfiniteQuery } from '@tanstack/react-query'

// 基础查询
const { data, isLoading, error, refetch } = useQuery({
  queryKey: ['todos'], // 查询键
  queryFn: fetchTodos,  // 查询函数
  enabled: true,        // 是否启用查询
  retry: 3,             // 重试次数
  staleTime: 5000,      // 数据保鲜时间(毫秒)
  cacheTime: 60000,     // 缓存时间
  refetchOnWindowFocus: true, // 窗口聚焦时重新获取
  refetchOnMount: true,       // 组件挂载时重新获取
})

// 并行查询
const results = useQueries({
  queries: [
    { queryKey: ['post', 1], queryFn: fetchPost },
    { queryKey: ['post', 2], queryFn: fetchPost }
  ]
})

// 无限滚动/分页查询
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
  queryKey: ['projects'],
  queryFn: fetchProjects,
  getNextPageParam: (lastPage) => lastPage.nextCursor,
})
修改相关
javascript 复制代码
import { useMutation, useQueryClient } from '@tanstack/react-query'

const queryClient = useQueryClient()

// 数据变更
const mutation = useMutation({
  mutationFn: addTodo,
  onSuccess: () => {
    // 成功回调
    queryClient.invalidateQueries({ queryKey: ['todos'] }) // 使缓存失效
  },
  onError: (error) => {
    // 错误处理
  },
  retry: 2,
})

缓存管理

javascript 复制代码
const queryClient = useQueryClient()

// 使查询失效并重新获取
queryClient.invalidateQueries({ 
  queryKey: ['todos'],
  exact: true // 精确匹配
})

// 使所有查询失效
queryClient.invalidateQueries()

// 获取查询数据
const data = queryClient.getQueryData(['todos', 1])

// 设置查询数据(手动更新缓存)
queryClient.setQueryData(['todos', 1], newData)

// 预取数据
await queryClient.prefetchQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
})

// 获取查询状态
const state = queryClient.getQueryState(['todos'])

// 移除查询(从缓存中删除)
queryClient.removeQueries({ queryKey: ['todos'] })

// 重置查询状态
queryClient.resetQueries({ queryKey: ['todos'] })

全局配置(QueryClient)

javascript 复制代码
import { QueryClient } from '@tanstack/react-query'

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

// 在App中提供
<QueryClientProvider client={queryClient}>
  <App />
</QueryClientProvider>

开发工具

javascript 复制代码
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'

// 在组件中使用
<ReactQueryDevtools initialIsOpen={false} />

其他实用 API

查询状态辅助
javascript 复制代码
const { 
  data, 
  isLoading,        // 初始加载
  isFetching,       // 任何获取中状态
  isError,          // 是否错误
  error,            // 错误对象
  isSuccess,        // 是否成功
  status,           // 'loading' | 'error' | 'success'
  isPaused,         // 网络离线时暂停
  isInitialLoading, // 第一次加载
  isRefetching,     // 重新获取中
  isStale,          // 数据是否过期
  failureCount,     // 失败次数
} = useQuery({...})
乐观更新
javascript 复制代码
useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    // 取消正在进行中的查询
    await queryClient.cancelQueries({ queryKey: ['todos'] })
    
    // 保存前一个状态
    const previousTodos = queryClient.getQueryData(['todos'])
    
    // 乐观更新
    queryClient.setQueryData(['todos'], old => [...old, newTodo])
    
    // 返回上下文用于错误回滚
    return { previousTodos }
  },
  onError: (err, newTodo, context) => {
    // 错误时回滚
    queryClient.setQueryData(['todos'], context.previousTodos)
  },
  onSettled: () => {
    // 完成后重新获取确保数据一致
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  }
})

常用的查询键模式

javascript 复制代码
// 基本
['todos']

// 带参数
['todo', id]

// 带过滤条件
['todos', { status: 'done', page: 1 }]

// 无限查询
['projects', 'infinite']

这些是 React Query 最常用的 API,涵盖了数据获取、缓存管理、状态更新等核心功能。

二、常用操作

🔄 数据获取与查询

基础查询
javascript 复制代码
// 1. 带参数的查询
const { data } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
  enabled: !!userId, // 条件查询
})

// 2. 依赖查询(一个查询依赖于另一个查询的结果)
const { data: user } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
})

const { data: projects } = useQuery({
  queryKey: ['projects', user?.id],
  queryFn: () => fetchUserProjects(user.id),
  enabled: !!user?.id, // 等待user加载完成
})
分页查询
javascript 复制代码
// 1. 基本分页
const [page, setPage] = useState(1)
const { data } = useQuery({
  queryKey: ['todos', page],
  queryFn: () => fetchTodos(page),
  keepPreviousData: true, // 保持上一页数据,平滑过渡
})

// 2. 无限滚动
const { 
  data, 
  fetchNextPage, 
  hasNextPage,
  isFetchingNextPage 
} = useInfiniteQuery({
  queryKey: ['projects'],
  queryFn: ({ pageParam = 1 }) => fetchProjects(pageParam),
  getNextPageParam: (lastPage, pages) => 
    lastPage.hasMore ? pages.length + 1 : undefined,
})

// 使用示例
<button 
  onClick={() => fetchNextPage()}
  disabled={!hasNextPage || isFetchingNextPage}
>
  {isFetchingNextPage ? '加载中...' : hasNextPage ? '加载更多' : '没有更多了'}
</button>

✏️ 数据修改与提交

基本提交
javascript 复制代码
const mutation = useMutation({
  mutationFn: (newTodo) => axios.post('/api/todos', newTodo),
  
  // 提交成功后自动刷新列表
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  },
  
  // 全局错误处理
  onError: (error) => {
    toast.error(`提交失败: ${error.message}`)
  }
})

// 使用
const handleSubmit = (data) => {
  mutation.mutate(data, {
    // 局部配置(覆盖全局)
    onSuccess: (data) => {
      console.log('创建成功:', data)
      navigate('/todos')
    }
  })
}
乐观更新(推荐👍)
javascript 复制代码
const mutation = useMutation({
  mutationFn: updateTodo,
  
  // 乐观更新三部曲
  onMutate: async (newTodo) => {
    // 1. 取消正在进行的查询,防止冲突
    await queryClient.cancelQueries({ queryKey: ['todos'] })
    
    // 2. 保存当前数据(用于回滚)
    const previousTodos = queryClient.getQueryData(['todos'])
    
    // 3. 立即更新UI
    queryClient.setQueryData(['todos'], old => 
      old.map(todo => 
        todo.id === newTodo.id ? { ...todo, ...newTodo } : todo
      )
    )
    
    return { previousTodos }
  },
  
  // 错误时回滚
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos'], context.previousTodos)
    toast.error('更新失败,已恢复')
  },
  
  // 无论成功失败,重新验证数据
  onSettled: () => {
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  }
})
批量操作
javascript 复制代码
// 并行执行多个mutation
const mutations = useMutation({
  mutationFn: async (ids) => {
    const results = await Promise.all(
      ids.map(id => deleteItem(id))
    )
    return results
  },
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['items'] })
  }
})

📊 缓存管理

主动更新缓存
javascript 复制代码
// 1. 添加新数据到列表
const addToCache = (newItem) => {
  queryClient.setQueryData(['items'], old => 
    old ? [...old, newItem] : [newItem]
  )
}

// 2. 更新单条数据
const updateCacheItem = (id, updates) => {
  queryClient.setQueryData(['items'], old =>
    old.map(item => 
      item.id === id ? { ...item, ...updates } : item
    )
  )
}

// 3. 删除缓存数据
const removeFromCache = (id) => {
  queryClient.setQueryData(['items'], old =>
    old.filter(item => item.id !== id)
  )
}
预加载数据
javascript 复制代码
// 1. 路由预加载
const preloadUserData = (userId) => {
  queryClient.prefetchQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
    staleTime: 60 * 1000, // 1分钟内有效
  })
}

// 2. 鼠标悬停时预加载
const handleMouseEnter = () => {
  queryClient.prefetchQuery({
    queryKey: ['post', postId],
    queryFn: () => fetchPost(postId),
  })
}

🎯 查询优化技巧

防抖/节流查询
javascript 复制代码
import { useDebounce } from 'use-debounce'

const [search, setSearch] = useState('')
const [debouncedSearch] = useDebounce(search, 500) // 防抖500ms

const { data } = useQuery({
  queryKey: ['search', debouncedSearch],
  queryFn: () => searchItems(debouncedSearch),
  enabled: debouncedSearch.length > 2, // 至少3个字符才搜索
})
依赖查询优化
javascript 复制代码
// 使用 enabled 控制查询顺序
const { data: user } = useQuery({
  queryKey: ['user', userId],
  queryFn: fetchUser,
})

const { data: permissions } = useQuery({
  queryKey: ['permissions', user?.role],
  queryFn: () => fetchPermissions(user.role),
  enabled: !!user?.role, // 只有user加载完成后才执行
})

const { data: settings } = useQuery({
  queryKey: ['settings', permissions?.level],
  queryFn: () => fetchSettings(permissions.level),
  enabled: !!permissions?.level,
})

🔧 常用配置模式

全局默认配置
javascript 复制代码
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 1, // 失败重试1次
      staleTime: 5 * 60 * 1000, // 5分钟数据保鲜
      cacheTime: 10 * 60 * 1000, // 10分钟缓存
      refetchOnWindowFocus: false, // 取消自动重获
      refetchOnMount: 'always', // 组件挂载时刷新
      refetchOnReconnect: true, // 网络重连时刷新
      retryDelay: (attemptIndex) => 
        Math.min(1000 * 2 ** attemptIndex, 30000), // 指数退避
    }
  }
})
请求取消
javascript 复制代码
const fetchTodo = async ({ signal }) => {
  const response = await fetch(`/api/todos/1`, { 
    signal, // 传入 signal
  })
  return response.json()
}

// React Query 会自动处理取消
const { data } = useQuery({
  queryKey: ['todo', 1],
  queryFn: fetchTodo,
})

// 组件卸载或查询键变化时,请求会自动取消
javascript 复制代码
import axios from 'axios'

// 使用 axios 的 CancelToken
const fetchUser = async ({ queryKey, signal }) => {
  const [, userId] = queryKey
  const response = await axios.get(`/api/users/${userId}`, {
    cancelToken: new axios.CancelToken(cancel => {
      // React Query 会调用 cancel 函数
      if (signal) {
        signal.addEventListener('abort', () => cancel())
      }
    })
  })
  return response.data
}

三、🚀 实战示例

表单提交 + 缓存更新

javascript 复制代码
function TodoForm() {
  const queryClient = useQueryClient()
  
  const mutation = useMutation({
    mutationFn: createTodo,
    onSuccess: (newTodo) => {
      // 1. 更新缓存
      queryClient.setQueryData(['todos'], old => [...old, newTodo])
      
      // 2. 可选:使后台查询失效,下次重新获取
      queryClient.invalidateQueries({
        queryKey: ['todos'],
        exact: true
      })
      
      // 3. 重置表单
      form.reset()
      
      // 4. 显示成功消息
      toast.success('创建成功!')
    },
    // 乐观更新(可选)
    onMutate: async (newTodo) => {
      await queryClient.cancelQueries(['todos'])
      const previous = queryClient.getQueryData(['todos'])
      queryClient.setQueryData(['todos'], old => [...old, { ...newTodo, id: Date.now() }])
      return { previous }
    },
    onError: (err, newTodo, context) => {
      queryClient.setQueryData(['todos'], context.previous)
      toast.error('创建失败')
    }
  })
  
  return (
    <form onSubmit={(e) => {
      e.preventDefault()
      mutation.mutate(formData)
    }}>
      <input disabled={mutation.isLoading} />
      <button type="submit" disabled={mutation.isLoading}>
        {mutation.isLoading ? '提交中...' : '提交'}
      </button>
    </form>
  )
}

分页 + 保持数据

javascript 复制代码
function TodoList() {
  const [page, setPage] = useState(1)
  
  const { data, isFetching, isLoading } = useQuery({
    queryKey: ['todos', { page }],
    queryFn: () => fetchTodos(page),
    keepPreviousData: true, // ✅ 关键:保持上一页数据
    staleTime: 5 * 60 * 1000,
  })
  
  return (
    <div>
      {isLoading ? (
        <div>首次加载...</div>
      ) : (
        <>
          <ul>
            {data?.items?.map(todo => (
              <li key={todo.id}>{todo.title}</li>
            ))}
          </ul>
          
          {/* 分页控件 */}
          <div>
            <button 
              onClick={() => setPage(old => Math.max(old - 1, 1))}
              disabled={page === 1}
            >
              上一页
            </button>
            
            <span>第 {page} 页</span>
            <span>{isFetching && ' 更新中...'}</span>
            
            <button
              onClick={() => setPage(old => old + 1)}
              disabled={!data?.hasMore}
            >
              下一页
            </button>
          </div>
        </>
      )}
    </div>
  )
}

四、📝 实践总结

  1. 查询键:使用数组格式,包含所有依赖项

  2. 条件查询 :善用 enabled 控制查询时机

  3. 乐观更新:提升用户体验

  4. 缓存策略 :合理设置 staleTimecacheTime

  5. 错误处理:全局 + 局部错误处理结合

  6. 性能优化 :使用 keepPreviousData、防抖等

  7. 开发工具:始终开启 React Query Devtools

这些操作涵盖了 React Query 90% 的日常使用场景,掌握后可以高效处理各种数据交互需求。

总结到此!

相关推荐
一 乐8 小时前
婚纱摄影网站|基于ssm + vue婚纱摄影网站系统(源码+数据库+文档)
前端·javascript·数据库·vue.js·spring boot·后端
C_心欲无痕8 小时前
ts - tsconfig.json配置讲解
linux·前端·ubuntu·typescript·json
清沫8 小时前
Claude Skills:Agent 能力扩展的新范式
前端·ai编程
yinuo9 小时前
前端跨页面通信终极指南:方案拆解、对比分析
前端
yinuo9 小时前
前端跨页面通讯终极指南⑨:IndexedDB 用法全解析
前端
xkxnq10 小时前
第二阶段:Vue 组件化开发(第 16天)
前端·javascript·vue.js
烛阴10 小时前
拒绝配置地狱!5 分钟搭建 Three.js + Parcel 完美开发环境
前端·webgl·three.js
Van_Moonlight10 小时前
RN for OpenHarmony 实战 TodoList 项目:空状态占位图
javascript·开源·harmonyos
xkxnq10 小时前
第一阶段:Vue 基础入门(第 15天)
前端·javascript·vue.js
anyup12 小时前
2026第一站:分享我在高德大赛现场学到的技术、产品与心得
前端·架构·harmonyos