译零 TransZero · TanStack Query 中文文档
主站

迁移到 React Query 3

更新于 2026-11-02 React 查看英文原文 ↗

React Query 的早期版本非常出色,为该库带来了诸多惊艳的新特性、更多魔法效果以及整体更佳的使用体验。它们也推动了库的大规模采用,并随之带来了海量的打磨反馈(问题报告与贡献),从而揭示出一些亟待进一步优化的环节,以让库变得更为完善。v3 版本正是这些打磨成果的集中体现。

概览

  • 更具可扩展性且更易测试的缓存配置
  • 更完善的 SSR 支持
  • 数据滞后(此前通过 usePaginatedQuery 实现)现已支持任意位置!
  • 双向无限查询(bi-directional Infinite Queries)
  • 查询数据选择器(Query data selectors)!
  • 可在使用前为查询和/或修改全面配置默认选项
  • 更精细的可选渲染优化粒度
  • 全新 useQueries Hook!(变长并行查询执行)
  • useIsFetching() Hook 现支持查询过滤器!
  • 修改支持重试 / 离线 / 重放
  • 可在 React 外部观察查询/修改
  • 可在任意位置使用 React Query 的核心逻辑!
  • 通过 react-query/devtools 提供捆绑式/同地化开发者工具(Devtools)
  • 缓存持久化至 Web 存储(实验性,通过 react-query/persistQueryClient-experimental 和 react-query/createWebStoragePersistor-experimental 实现)

破坏性变更

QueryCache 已拆分为 QueryClient 以及更低层级的 QueryCache 和 MutationCache 实例

QueryCache 包含全部查询,MutationCache 包含全部修改,而 QueryClient 可用于设置配置并与其交互。

此举带来如下优势:

  • 支持不同类型缓存。
  • 多个具有不同配置的客户端可共享同一缓存。
  • 客户端可用于跟踪查询,从而在 SSR 中实现共享缓存。
  • 客户端 API 更聚焦于通用场景。
  • 各组件更易于单独测试。

创建 new QueryClient() 时,若未显式提供 QueryCache 和 MutationCache,系统将自动为你创建。

TypeScript
1import { QueryClient } from 'react-query'2 3const queryClient = new QueryClient()

ReactQueryConfigProvider 和 ReactQueryCacheProvider 均已被 QueryClientProvider 取代

查询与修改的默认选项现可在 QueryClient 中指定:

请注意:现为 defaultOptions,而非此前的 defaultConfig

TypeScript
1const queryClient = new QueryClient({2  defaultOptions: {3    queries: {4      // query options5    },6    mutations: {7      // mutation options8    },9  },10})

QueryClientProvider 组件现用于将 QueryClient 连接到你的应用:

TypeScript
1import { QueryClient, QueryClientProvider } from 'react-query'2 3const queryClient = new QueryClient()4 5function App() {6  return <QueryClientProvider client={queryClient}>...</QueryClientProvider>7}

默认 QueryCache 已彻底移除。这次是动真格的!

如先前弃用警告所提示,主包中不再创建或导出默认 QueryCache。你必须自行通过 new QueryClient() 或 new QueryCache() 创建(随后可将其传入 new QueryClient({ queryCache }))

已弃用的 makeQueryCache 工具函数已被移除。

它早已名存实亡,如今终于正式退出历史舞台 :)

QueryCache.prefetchQuery() 已迁移至 QueryClient.prefetchQuery()

新的 QueryClient.prefetchQuery() 函数为异步函数,但不返回查询数据。若需获取数据,请改用新的 QueryClient.fetchQuery() 函数。

TypeScript
1// Prefetch a query:2await queryClient.prefetchQuery('posts', fetchPosts)3 4// Fetch a query:5try {6  const data = await queryClient.fetchQuery('posts', fetchPosts)7} catch (error) {8  // Error handling9}

ReactQueryErrorResetBoundary 和 QueryCache.resetErrorBoundaries() 已被 QueryErrorResetBoundary 和 useQueryErrorResetBoundary() 取代。

二者共同提供与此前一致的体验,同时新增控制能力,使你能精确选择希望重置的组件树。详情请参阅:

QueryCache.getQuery() 已被 QueryCache.find() 取代。

现应使用 QueryCache.find() 从缓存中查找单个查询。

QueryCache.getQueries() 已迁移至 QueryCache.findAll()。

现应使用 QueryCache.findAll() 从缓存中查找多个查询。

QueryCache.isFetching 已迁移至 QueryClient.isFetching()。

请注意:现为函数而非属性

useQueryCache Hook 已被 useQueryClient Hook 取代。

它为其所在组件树返回所提供的 queryClient,除重命名外通常无需额外调整。

查询键各部分/片段不再自动展开并传入查询函数。

内联函数现为向查询函数传递参数的推荐方式:

TypeScript
1// Old2useQuery(['post', id], (_key, id) => fetchPost(id))3 4// New5useQuery(['post', id], () => fetchPost(id))

若你仍坚持不使用内联函数,可利用新传入的 QueryFunctionContext:

TypeScript
1useQuery(['post', id], (context) => fetchPost(context.queryKey[1]))

无限查询页参数现通过 QueryFunctionContext.pageParam 传入。

此前它们作为查询函数中查询键的最后一个参数传入,但此方式对某些模式而言过于困难。

TypeScript
1// Old2useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam))3 4// New5useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPosts(pageParam))

usePaginatedQuery() 已被 keepPreviousData 选项取代。

新的 keepPreviousData 选项同时适用于 useQuery 和 useInfiniteQuery,并能为你的数据提供相同的“滞后”效果:

TypeScript
1import { useQuery } from 'react-query'2 3function Page({ page }) {4  const { data } = useQuery(['page', page], fetchPage, {5    keepPreviousData: true,6  })7}

useInfiniteQuery() 现支持双向操作

useInfiniteQuery() 接口已重构,以完整支持双向无限列表。

  • options.getFetchMore 已重命名为 options.getNextPageParam
  • queryResult.canFetchMore 已重命名为 queryResult.hasNextPage
  • queryResult.fetchMore 已重命名为 queryResult.fetchNextPage
  • queryResult.isFetchingMore 已重命名为 queryResult.isFetchingNextPage
  • 新增 options.getPreviousPageParam 选项
  • 新增 queryResult.hasPreviousPage 属性
  • 新增 queryResult.fetchPreviousPage 属性
  • 新增 queryResult.isFetchingPreviousPage
  • 无限查询的 data 现为对象,包含用于获取各页的 pages 和 pageParams:{ pages: [data, data, data], pageParams: [...]}

单向示例:

TypeScript
1const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =2  useInfiniteQuery(3    'projects',4    ({ pageParam = 0 }) => fetchProjects(pageParam),5    {6      getNextPageParam: (lastPage, pages) => lastPage.nextCursor,7    },8  )

双向示例:

TypeScript
1const {2  data,3  fetchNextPage,4  fetchPreviousPage,5  hasNextPage,6  hasPreviousPage,7  isFetchingNextPage,8  isFetchingPreviousPage,9} = useInfiniteQuery(10  'projects',11  ({ pageParam = 0 }) => fetchProjects(pageParam),12  {13    getNextPageParam: (lastPage, pages) => lastPage.nextCursor,14    getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,15  },16)

反向单向示例:

TypeScript
1const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =2  useInfiniteQuery(3    'projects',4    ({ pageParam = 0 }) => fetchProjects(pageParam),5    {6      select: (data) => ({7        pages: [...data.pages].reverse(),8        pageParams: [...data.pageParams].reverse(),9      }),10      getNextPageParam: (lastPage, pages) => lastPage.nextCursor,11    },12  )

无限查询数据现包含页面数组及用于获取这些页面的 pageParams。

这使得数据与 pageParams 更易操作,例如可同时移除首页数据及其对应参数:

TypeScript
1queryClient.setQueryData(['projects'], (data) => ({2  pages: data.pages.slice(1),3  pageParams: data.pageParams.slice(1),4}))

useMutation 现返回对象而非数组

尽管旧方式曾让我们初识 useState 时倍感亲切,但那份温暖并未持续太久。如今修改返回值为单一对象。

TypeScript
1// Old:2const [mutate, { status, reset }] = useMutation()3 4// New:5const { mutate, status, reset } = useMutation()

mutation.mutate 不再返回 Promise

  • [mutate] 变量已更改为 mutation.mutate 函数
  • 新增 mutation.mutateAsync 函数

用户常误以为该 Promise 行为与常规 Promise 一致,由此引发大量疑问。

因此 mutate 函数现已拆分为 mutate 和 mutateAsync 两个函数。

使用回调时可调用 mutate 函数:

TypeScript
1const { mutate } = useMutation({ mutationFn: addTodo })2 3mutate('todo', {4  onSuccess: (data) => {5    console.log(data)6  },7  onError: (error) => {8    console.error(error)9  },10  onSettled: () => {11    console.log('settled')12  },13})

使用 async/await 时可调用 mutateAsync 函数:

TypeScript
1const { mutateAsync } = useMutation({ mutationFn: addTodo })2 3try {4  const data = await mutateAsync('todo')5  console.log(data)6} catch (error) {7  console.error(error)8} finally {9  console.log('settled')10}

useQuery 的对象语法现采用扁平化配置:

TypeScript
1// Old:2useQuery({3  queryKey: 'posts',4  queryFn: fetchPosts,5  config: { staleTime: Infinity },6})7 8// New:9useQuery({10  queryKey: 'posts',11  queryFn: fetchPosts,12  staleTime: Infinity,13})

若设置 QueryOptions.enabled 选项,则其值必须为布尔值(true/false)

enabled 查询选项仅在值为 false 时禁用查询。 如有需要,可使用 !!userId 或 Boolean(userId) 进行类型转换;若传入非布尔值,系统将抛出清晰的错误提示。

QueryOptions.initialStale 选项已被移除

initialStale 查询选项已被移除,初始数据现被视为常规数据。 这意味着若提供了 initialData,查询将在挂载时默认重新拉取。 若不希望立即重新拉取,可定义 staleTime。

QueryOptions.forceFetchOnMount 选项已被 refetchOnMount: 'always' 取代

诚然,我们此前累积了过多 refetchOn____ 选项,此次调整将大幅精简。

QueryOptions.refetchOnMount 选项现仅作用于其父组件,而非全部查询观察者

当 refetchOnMount 设为 false 时,此前会阻止所有额外组件在挂载时重新拉取。 在 v3 中,仅设置了该选项的组件不会在挂载时重新拉取。

QueryOptions.queryFnParamsFilter 已被移除,由新 QueryFunctionContext 对象替代。

queryFnParamsFilter 选项已被移除,因为查询函数现接收 QueryFunctionContext 对象而非查询键。

你仍可在查询函数内部对参数进行筛选,因 QueryFunctionContext 同样包含查询键。

QueryOptions.notifyOnStatusChange 选项已被新 notifyOnChangeProps 和 notifyOnChangePropsExclusions 选项取代。

借助这两个新选项,你可对组件何时重新渲染进行细粒度控制。

仅当 data 或 error 属性变更时重新渲染:

TypeScript
1import { useQuery } from 'react-query'2 3function User() {4  const { data } = useQuery(['user'], fetchUser, {5    notifyOnChangeProps: ['data', 'error'],6  })7  return <div>Username: {data.username}</div>8}

当 isStale 属性变更时禁止重新渲染:

TypeScript
1import { useQuery } from 'react-query'2 3function User() {4  const { data } = useQuery(['user'], fetchUser, {5    notifyOnChangePropsExclusions: ['isStale'],6  })7  return <div>Username: {data.username}</div>8}

QueryResult.clear() 函数已重命名为 QueryResult.remove()

尽管此前名为 clear,其实质仅为从缓存中移除查询。新名称更准确反映其功能。

QueryResult.updatedAt 属性已拆分为 QueryResult.dataUpdatedAt 和 QueryResult.errorUpdatedAt 属性

由于数据与错误可能同时存在,updatedAt 属性已拆分为 dataUpdatedAt 和 errorUpdatedAt。

setConsole() 已被新 setLogger() 函数取代

TypeScript
1import { setLogger } from 'react-query'2 3// Log with Sentry4setLogger({5  error: (error) => {6    Sentry.captureException(error)7  },8})9 10// Log with Winston11setLogger(winston.createLogger())

React Native 不再需要手动覆写日志器

为避免 React Native 中查询失败时显示错误界面,此前需手动修改 Console:

TypeScript
1import { setConsole } from 'react-query'2 3setConsole({4  log: console.log,5  warn: console.warn,6  error: console.warn,7})

在 v3 中,当 React Query 在 React Native 中运行时,此操作将自动完成。

TypeScript

QueryStatus 已从 枚举 更改为 联合类型

因此,若你此前通过 QueryStatus 枚举属性检查查询或修改的 status 属性,则现需改为检查该枚举此前各属性所对应的字符串字面量。

故需将枚举属性替换为其等效字符串字面量,例如:

  • QueryStatus.Idle → 'idle'
  • QueryStatus.Loading → 'loading'
  • QueryStatus.Error → 'error'
  • QueryStatus.Success → 'success'

以下为你需进行的变更示例:

TypeScript
1- import { useQuery, QueryStatus } from 'react-query'; // [!code --]2+ import { useQuery } from 'react-query'; // [!code ++]3 4const { data, status } = useQuery(['post', id], () => fetchPost(id))5 6- if (status === QueryStatus.Loading) { // [!code --]7+ if (status === 'loading') { // [!code ++]8  ...9}10 11- if (status === QueryStatus.Error) { // [!code --]12+ if (status === 'error') { // [!code ++]13  ...14}

新特性

查询数据选择器

useQuery 和 useInfiniteQuery Hook 现支持 select 选项,用于选取或转换查询结果的部分内容。

TypeScript
1import { useQuery } from 'react-query'2 3function User() {4  const { data } = useQuery(['user'], fetchUser, {5    select: (user) => user.username,6  })7  return <div>Username: {data}</div>8}

将 notifyOnChangeProps 选项设为 ['data', 'error'],即可仅在所选数据变更时触发重新渲染。

useQueries() Hook:支持变长并行查询执行

想在循环中执行 useQuery?Hook 规则禁止如此操作,但借助全新的 useQueries() Hook,你完全可以!

TypeScript
1import { useQueries } from 'react-query'2 3function Overview() {4  const results = useQueries([5    { queryKey: ['post', 1], queryFn: fetchPost },6    { queryKey: ['post', 2], queryFn: fetchPost },7  ])8  return (9    <ul>10      {results.map(({ data }) => data && <li key={data.id}>{data.title})</li>)}11    </ul>12  )13}

重试/离线修改

默认情况下 React Query 不会在修改出错时重试,但可通过 retry 选项启用:

TypeScript
1const mutation = useMutation({2  mutationFn: addTodo,3  retry: 3,4})

若修改因设备离线而失败,设备重连后将按原顺序重试。

持久化修改

修改现可持久化至存储并在后续恢复。更多信息请参阅修改文档。

QueryObserver

可使用 QueryObserver 创建和/或监听查询:

TypeScript
1const observer = new QueryObserver(queryClient, { queryKey: 'posts' })2 3const unsubscribe = observer.subscribe((result) => {4  console.log(result)5  unsubscribe()6})

InfiniteQueryObserver

可使用 InfiniteQueryObserver 创建和/或监听无限查询:

TypeScript
1const observer = new InfiniteQueryObserver(queryClient, {2  queryKey: 'posts',3  queryFn: fetchPosts,4  getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,5  getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,6})7 8const unsubscribe = observer.subscribe((result) => {9  console.log(result)10  unsubscribe()11})

QueriesObserver

可使用 QueriesObserver 创建和/或监听多个查询:

TypeScript
1const observer = new QueriesObserver(queryClient, [2  { queryKey: ['post', 1], queryFn: fetchPost },3  { queryKey: ['post', 2], queryFn: fetchPost },4])5 6const unsubscribe = observer.subscribe((result) => {7  console.log(result)8  unsubscribe()9})

为特定查询设置默认选项

可使用 QueryClient.setQueryDefaults() 方法为特定查询设置默认选项:

TypeScript
1queryClient.setQueryDefaults(['posts'], { queryFn: fetchPosts })2 3function Component() {4  const { data } = useQuery(['posts'])5}

为特定修改设置默认选项

可使用 QueryClient.setMutationDefaults() 方法为特定修改设置默认选项:

TypeScript
1queryClient.setMutationDefaults(['addPost'], { mutationFn: addPost })2 3function Component() {4  const { mutate } = useMutation({ mutationKey: ['addPost'] })5}

useIsFetching()

useIsFetching() Hook 现支持过滤器,例如可仅对特定类型查询显示加载指示器:

TypeScript
1const fetches = useIsFetching({ queryKey: ['posts'] })

核心分离

React Query 的核心现已完全与 React 解耦,因此亦可独立使用或集成至其他框架。仅导入核心功能,请使用 react-query/core 入口点:

TypeScript
1import { QueryClient } from 'react-query/core'

开发者工具现已整合至主仓库及 npm 包

开发者工具现已直接包含于 react-query 包中,导入路径为 react-query/devtools。只需将 react-query-devtools 导入替换为 react-query/devtools 即可。

本页为 TanStack Query 文档(React 版)的非官方中文翻译,原文由上游团队维护,遵循 MIT 许可。 译文如有偏差,以英文原文为准。

译自上游 59d11de