queryClient.getQueryData
现在仅接受 queryKey 作为参数queryClient.getQueryState
现在仅接受 queryKey 作为参数refetchInterval
的回调函数仅接收 query
作为参数remove
方法已被移除isDataEqual
选项已被移除cacheTime
重命名为 gcTime
useErrorBoundary
选项已重命名为 throwOnError
Error
现在是错误的默认类型,而不是 unknown
prefer-query-object-syntax
规则已移除keepPreviousData
,转而使用 placeholderData
身份函数focus
事件navigator.onLine
属性context
属性,转而使用自定义 queryClient
实例refetchPage
,转而使用 maxPages
dehydrate
APIinitialPageParam
getNextPageParam
或 getPreviousPageParam
返回 null
现在表示没有更多页面可用status: loading
已更改为 status: pending
,isLoading
已更改为 isPending
,isInitialLoading
已重命名为 isLoading
hashQueryKey
已重命名为 hashKey
contextSharing
属性已从 QueryClientProvider 中移除unstable_batchedUpdates
用作 React 和 React Native 中的批处理函数useQueries
的新 combine
选项fine grained storage persister
v5 是一个主版本,所以有一些破坏性更改需要注意
useQuery 及其相关 hook 在 TypeScript 中曾有许多重载:函数可以有不同的调用方式。这不仅在类型上难以维护,还需要运行时检查第一个和第二个参数的类型,才能正确创建选项。
现在我们只支持对象格式。
useQuery(key, fn, options) // [!code --]
useQuery({ queryKey, queryFn, ...options }) // [!code ++]
useInfiniteQuery(key, fn, options) // [!code --]
useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
useMutation(fn, options) // [!code --]
useMutation({ mutationFn, ...options }) // [!code ++]
useIsFetching(key, filters) // [!code --]
useIsFetching({ queryKey, ...filters }) // [!code ++]
useIsMutating(key, filters) // [!code --]
useIsMutating({ mutationKey, ...filters }) // [!code ++]
useQuery(key, fn, options) // [!code --]
useQuery({ queryKey, queryFn, ...options }) // [!code ++]
useInfiniteQuery(key, fn, options) // [!code --]
useInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
useMutation(fn, options) // [!code --]
useMutation({ mutationFn, ...options }) // [!code ++]
useIsFetching(key, filters) // [!code --]
useIsFetching({ queryKey, ...filters }) // [!code ++]
useIsMutating(key, filters) // [!code --]
useIsMutating({ mutationKey, ...filters }) // [!code ++]
queryClient.isFetching(key, filters) // [!code --]
queryClient.isFetching({ queryKey, ...filters }) // [!code ++]
queryClient.ensureQueryData(key, filters) // [!code --]
queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++]
queryClient.getQueriesData(key, filters) // [!code --]
queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++]
queryClient.setQueriesData(key, updater, filters, options) // [!code --]
queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++]
queryClient.removeQueries(key, filters) // [!code --]
queryClient.removeQueries({ queryKey, ...filters }) // [!code ++]
queryClient.resetQueries(key, filters, options) // [!code --]
queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.cancelQueries(key, filters, options) // [!code --]
queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.invalidateQueries(key, filters, options) // [!code --]
queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.refetchQueries(key, filters, options) // [!code --]
queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.fetchQuery(key, fn, options) // [!code --]
queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchQuery(key, fn, options) // [!code --]
queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.fetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.isFetching(key, filters) // [!code --]
queryClient.isFetching({ queryKey, ...filters }) // [!code ++]
queryClient.ensureQueryData(key, filters) // [!code --]
queryClient.ensureQueryData({ queryKey, ...filters }) // [!code ++]
queryClient.getQueriesData(key, filters) // [!code --]
queryClient.getQueriesData({ queryKey, ...filters }) // [!code ++]
queryClient.setQueriesData(key, updater, filters, options) // [!code --]
queryClient.setQueriesData({ queryKey, ...filters }, updater, options) // [!code ++]
queryClient.removeQueries(key, filters) // [!code --]
queryClient.removeQueries({ queryKey, ...filters }) // [!code ++]
queryClient.resetQueries(key, filters, options) // [!code --]
queryClient.resetQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.cancelQueries(key, filters, options) // [!code --]
queryClient.cancelQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.invalidateQueries(key, filters, options) // [!code --]
queryClient.invalidateQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.refetchQueries(key, filters, options) // [!code --]
queryClient.refetchQueries({ queryKey, ...filters }, options) // [!code ++]
queryClient.fetchQuery(key, fn, options) // [!code --]
queryClient.fetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchQuery(key, fn, options) // [!code --]
queryClient.prefetchQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.fetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.fetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryClient.prefetchInfiniteQuery(key, fn, options) // [!code --]
queryClient.prefetchInfiniteQuery({ queryKey, queryFn, ...options }) // [!code ++]
queryCache.find(key, filters) // [!code --]
queryCache.find({ queryKey, ...filters }) // [!code ++]
queryCache.findAll(key, filters) // [!code --]
queryCache.findAll({ queryKey, ...filters }) // [!code ++]
queryCache.find(key, filters) // [!code --]
queryCache.find({ queryKey, ...filters }) // [!code ++]
queryCache.findAll(key, filters) // [!code --]
queryCache.findAll({ queryKey, ...filters }) // [!code ++]
queryClient.getQueryData 的参数已更改为仅接受 queryKey
queryClient.getQueryData(queryKey, filters) // [!code --]
queryClient.getQueryData(queryKey) // [!code ++]
queryClient.getQueryData(queryKey, filters) // [!code --]
queryClient.getQueryData(queryKey) // [!code ++]
queryClient.getQueryState 的参数已更改为仅接受 queryKey
queryClient.getQueryState(queryKey, filters) // [!code --]
queryClient.getQueryState(queryKey) // [!code ++]
queryClient.getQueryState(queryKey, filters) // [!code --]
queryClient.getQueryState(queryKey) // [!code ++]
为了使 remove 重载迁移更容易,v5 提供了一个代码转换工具。
代码转换工具是尽最大努力帮助您迁移破坏性更改的尝试。请仔细审查生成的代码!另外,有一些代码转换工具无法发现的边缘情况,因此请留意日志输出。
如果您想在 .js 或 .jsx 文件上运行它,请使用下面的命令
npx jscodeshift@latest ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
npx jscodeshift@latest ./path/to/src/ \
--extensions=js,jsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
如果您想在 .ts 或 .tsx 文件上运行它,请使用下面的命令
npx jscodeshift@latest ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
npx jscodeshift@latest ./path/to/src/ \
--extensions=ts,tsx \
--parser=tsx \
--transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs
请注意,在 TypeScript 的情况下,您需要使用 tsx 作为解析器;否则,代码转换工具将无法正确应用!
注意:应用代码转换工具可能会破坏您的代码格式,因此请在应用代码转换工具后不要忘记运行 prettier 和/或 eslint!
关于代码转换工具工作原理的一些说明
onSuccess、onError 和 onSettled 已从 Queries 中移除。它们未对 Mutations 进行修改。请参阅 此 RFC 以了解此更改背后的动机以及如何进行替代。
refetchInterval
的回调函数仅接收 query 作为参数这简化了回调函数的调用方式(refetchOnWindowFocus、refetchOnMount 和 refetchOnReconnect 回调也都只将 query 作为参数传递),并且修复了当回调函数通过 select 转换数据时的一些类型问题。
- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --]
+ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++]
- refetchInterval: number | false | ((data: TData | undefined, query: Query) => number | false | undefined) // [!code --]
+ refetchInterval: number | false | ((query: Query) => number | false | undefined) // [!code ++]
您仍然可以通过 query.state.data 访问数据,但它将不是通过 select 转换后的数据。如果您需要访问转换后的数据,可以对 query.state.data 再次调用转换。
以前,remove 方法用于从 queryCache 中删除查询,而不会通知观察者。它最适合用于命令式地删除不再需要的数据,例如在用户注销时。
但是,当查询仍然处于活动状态时,这样做没有太大意义,因为它只会触发下一次重新渲染时的硬加载状态。
如果您仍然需要删除查询,可以使用 queryClient.removeQueries({queryKey: key})
const queryClient = useQueryClient()
const query = useQuery({ queryKey, queryFn })
query.remove() // [!code --]
queryClient.removeQueries({ queryKey }) // [!code ++]
const queryClient = useQueryClient()
const query = useQuery({ queryKey, queryFn })
query.remove() // [!code --]
queryClient.removeQueries({ queryKey }) // [!code ++]
主要是因为在类型推断方面发布了一个重要的修复。请参阅此 TypeScript issue 以获取更多信息。
以前,此函数用于指示是使用旧的 data(true)还是新的 data(false)作为查询的解析数据。
您可以通过将函数传递给 structuralSharing 来实现相同的功能
import { replaceEqualDeep } from '@tanstack/react-query'
- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --]
+ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++]
import { replaceEqualDeep } from '@tanstack/react-query'
- isDataEqual: (oldData, newData) => customCheck(oldData, newData) // [!code --]
+ structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData) // [!code ++]
自定义日志记录器在 4.0 版本中已被弃用,在此版本中已被移除。日志记录仅在开发模式下有效,在这种模式下,无需传递自定义日志记录器。
我们已更新浏览器列表以生成更现代、更高效、更小的包。您可以在 此处 阅读有关要求的信息。
TanStack Query 一直在类上拥有私有字段和方法,但它们并不真正私有 - 它们只是在 TypeScript 中是私有的。我们现在使用 ECMAScript 私有类特性,这意味着这些字段现在真正私有,在运行时无法从外部访问。
几乎所有人都误解了 cacheTime 的含义。它听起来像是“数据缓存的时间”,但这并不正确。
只要查询仍在被使用,cacheTime 就不会起任何作用。它只在查询变得未使用后才开始生效。时间过后,数据将被“垃圾回收”,以防止缓存不断增长。
gc 指的是“垃圾回收”时间。它有点技术性,但在计算机科学中也是一个众所周知的缩写。
const MINUTE = 1000 * 60;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- cacheTime: 10 * MINUTE, // [!code --]
+ gcTime: 10 * MINUTE, // [!code ++]
},
},
})
const MINUTE = 1000 * 60;
const queryClient = new QueryClient({
defaultOptions: {
queries: {
- cacheTime: 10 * MINUTE, // [!code --]
+ gcTime: 10 * MINUTE, // [!code ++]
},
},
})
useErrorBoundary
选项已重命名为 throwOnError为了使 useErrorBoundary 选项更具框架无关性,并避免与 React hook 前缀“use”以及“ErrorBoundary”组件名称产生混淆,它已重命名为 throwOnError,以更准确地反映其功能。
尽管在 JavaScript 中,您可以throw 任何东西(这使得 unknown 成为最正确的类型),但几乎总是,Errors(或 Error 的子类)被 throw。此更改使得在 TypeScript 中处理 error 字段在大多数情况下更加容易。
如果您想 throw 非 Error 类型的内容,您现在需要自己设置泛型
useQuery<number, string>({
queryKey: ['some-query'],
queryFn: async () => {
if (Math.random() > 0.5) {
throw 'some error'
}
return 42
},
})
useQuery<number, string>({
queryKey: ['some-query'],
queryFn: async () => {
if (Math.random() > 0.5) {
throw 'some error'
}
return 42
},
})
要全局设置不同类型的错误,请参阅 TypeScript 指南。
由于现在唯一支持的语法是对象语法,因此此规则不再需要
我们已移除 keepPreviousData 选项和 isPreviousData 标志,因为它们的功能与 placeholderData 和 isPlaceholderData 标志非常相似。
为了实现与 keepPreviousData 相同的功能,我们在 placeholderData 中添加了前一个查询 data 作为参数,该参数接受一个身份函数。因此,您只需为 placeholderData 提供一个身份函数,或者使用 Tanstack Query 中包含的 keepPreviousData 函数。
此处需要注意的是,useQueries 在 placeholderData 函数中不会收到 previousData 作为参数。这是由于传递在数组中的查询的动态性质,这可能导致 placeholder 和 queryFn 的结果形状不同。
import {
useQuery,
+ keepPreviousData // [!code ++]
} from "@tanstack/react-query";
const {
data,
- isPreviousData, // [!code --]
+ isPlaceholderData, // [!code ++]
} = useQuery({
queryKey,
queryFn,
- keepPreviousData: true, // [!code --]
+ placeholderData: keepPreviousData // [!code ++]
});
import {
useQuery,
+ keepPreviousData // [!code ++]
} from "@tanstack/react-query";
const {
data,
- isPreviousData, // [!code --]
+ isPlaceholderData, // [!code ++]
} = useQuery({
queryKey,
queryFn,
- keepPreviousData: true, // [!code --]
+ placeholderData: keepPreviousData // [!code ++]
});
在 Tanstack Query 的上下文中,身份函数是指一个始终返回其提供的参数(即数据)而不改变的函数。
useQuery({
queryKey,
queryFn,
placeholderData: (previousData, previousQuery) => previousData, // identity function with the same behaviour as `keepPreviousData`
})
useQuery({
queryKey,
queryFn,
placeholderData: (previousData, previousQuery) => previousData, // identity function with the same behaviour as `keepPreviousData`
})
此更改存在一些必须注意的注意事项
placeholderData 总是会将您置于 success 状态,而 keepPreviousData 会为您提供前一个查询的状态。该状态可能是 error,如果我们成功获取了数据,然后在后台重试时出现错误。但是,错误本身并未共享,因此我们决定坚持 placeholderData 的行为。
keepPreviousData 提供了前一个数据的 dataUpdatedAt 时间戳,而使用 placeholderData 时,dataUpdatedAt 将保持为 0。如果您想在屏幕上连续显示该时间戳,这可能会令人讨厌。但是,您可以使用 useEffect 来解决。
const [updatedAt, setUpdatedAt] = useState(0)
const { data, dataUpdatedAt } = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
})
useEffect(() => {
if (dataUpdatedAt > updatedAt) {
setUpdatedAt(dataUpdatedAt)
}
}, [dataUpdatedAt])
const [updatedAt, setUpdatedAt] = useState(0)
const { data, dataUpdatedAt } = useQuery({
queryKey: ['projects', page],
queryFn: () => fetchProjects(page),
})
useEffect(() => {
if (dataUpdatedAt > updatedAt) {
setUpdatedAt(dataUpdatedAt)
}
}, [dataUpdatedAt])
现在仅使用 visibilitychange 事件。这是可能的,因为我们仅支持支持 visibilitychange 事件的浏览器。这修复了此处列出的许多问题。
navigator.onLine 在基于 Chromium 的浏览器中效果不佳。关于假阴性,存在大量问题,导致查询被错误地标记为 offline。
为了规避这个问题,我们现在始终以 online: true 开始,并且只监听 online 和 offline 事件来更新状态。
这应该会降低假阴性的可能性,但是,对于通过 serviceWorkers 加载的离线应用,这可能意味着假阳性,这些应用即使没有互联网连接也能工作。
在 v4 中,我们引入了将自定义 context 传递给所有 react-query hook 的可能性。这允许在微前端中使用时进行适当的隔离。
但是,context 是一个仅限 React 的功能。所有 context 所做的事情就是让我们能够访问 queryClient。我们可以通过允许直接传递自定义 queryClient 来实现相同的隔离。反过来,这将使其他框架能够以框架无关的方式获得相同的功能。
import { queryClient } from './my-client'
const { data } = useQuery(
{
queryKey: ['users', id],
queryFn: () => fetch(...),
- context: customContext // [!code --]
},
+ queryClient, // [!code ++]
)
import { queryClient } from './my-client'
const { data } = useQuery(
{
queryKey: ['users', id],
queryFn: () => fetch(...),
- context: customContext // [!code --]
},
+ queryClient, // [!code ++]
)
在 v4 中,我们引入了通过 refetchPage 函数为无限查询定义重取页面的可能性。
但是,重取所有页面可能会导致 UI 不一致。此外,此选项在 queryClient.refetchQueries 等地方可用,但它只对无限查询有效,而对“普通”查询无效。
v5 包含了一个用于无限查询的新 maxPages 选项,用于限制存储在查询数据中和后续重取的页面数量。这个新功能处理了最初为 refetchPage 页面功能识别的用例,而没有相关的问
可以传递给 dehydrate 的选项已得到简化。Queries 和 Mutations 总是会被脱水(根据默认函数实现)。要更改此行为,而不是使用已移除的布尔选项 dehydrateMutations 和 dehydrateQueries,您可以改为实现函数等价项 shouldDehydrateQuery 或 shouldDehydrateMutation。要获得以前不进行查询/突变脱水行为,请传入 () => false。
- dehydrateMutations?: boolean // [!code --]
- dehydrateQueries?: boolean // [!code --]
- dehydrateMutations?: boolean // [!code --]
- dehydrateQueries?: boolean // [!code --]
以前,我们将 undefined 传递给 queryFn 作为 pageParam,您可以在 queryFn 函数签名中为 pageParam 参数分配默认值。这有一个缺点,即在 queryCache 中存储 undefined,这是不可序列化的。
相反,您现在必须向无限查询选项传递一个显式的 initialPageParam。它将用作第一页的 pageParam
useInfiniteQuery({
queryKey,
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam), // [!code --]
+ queryFn: ({ pageParam }) => fetchSomething(pageParam), // [!code ++]
+ initialPageParam: 0, // [!code ++]
getNextPageParam: (lastPage) => lastPage.next,
})
useInfiniteQuery({
queryKey,
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam), // [!code --]
+ queryFn: ({ pageParam }) => fetchSomething(pageParam), // [!code ++]
+ initialPageParam: 0, // [!code ++]
getNextPageParam: (lastPage) => lastPage.next,
})
以前,我们允许通过将 pageParam 值直接传递给 fetchNextPage 或 fetchPreviousPage 来覆盖 getNextPageParam 或 getPreviousPageParam 返回的 pageParams。此功能与重取完全不兼容,并且不广为人知或使用。这也意味着 getNextPageParam 现在是无限查询所必需的。
在 v4 中,您需要明确返回 undefined 来指示没有更多页面可用。我们将此检查范围扩大到包括 null。
在服务器上,retry 现在默认为 0 而不是 3。对于预取,我们始终默认为 0 次重试,但由于启用了 suspense 的查询现在也可以直接在服务器上执行(自 React18 以来),我们必须确保不在服务器上重试。
loading
状态已重命名为 pending,同样,派生的 isLoading 标志已重命名为 isPending。
对于 mutations 也是如此,status 已从 loading 更改为 pending,并且 isLoading 标志已更改为 isPending。
最后,为查询添加了一个新的派生 isLoading 标志,实现为 isPending && isFetching。这意味着 isLoading 和 isInitialLoading 具有相同的功能,但 isInitialLoading 已弃用,将在下一个主版本中移除。
要了解此更改背后的原因,请查看 v5 路线图讨论。
因为它也哈希 mutation keys,并且可以在 useIsMutating 和 useMutationState 的 predicate 函数中使用,这些函数会传递 mutations。
React Query v5 需要 React 18.0 或更高版本。这是因为我们使用了新的 useSyncExternalStore hook,它仅在 React 18.0 及更高版本中可用。以前,我们一直使用 React 提供的 shim。
contextSharing
属性已从 QueryClientProvider 中移除以前,您可以使用 contextSharing 属性来跨窗口共享第一个(至少一个)query client 实例。这确保了如果 TanStack Query 在不同的 bundle 或微前端中使用,它们都将使用相同的 context 实例,而不管模块作用域。
随着 v5 中自定义 context 属性的移除,请参阅关于“移除了自定义 context 属性,转而使用自定义 queryClient 实例”的部分。如果您希望在应用程序的多个包之间共享同一个 query client,您可以直接传递一个共享的自定义 queryClient 实例。
由于 unstable_batchedUpdates 函数在 React 18 中是 noop,因此它将不再自动设置为 react-query 中的批处理函数。
如果您的框架支持自定义批处理函数,您可以通过调用 notifyManager.setBatchNotifyFunction 来告知 TanStack Query。
例如,这是 solid-query 中设置批处理函数的方式
import { notifyManager } from '@tanstack/query-core'
import { batch } from 'solid-js'
notifyManager.setBatchNotifyFunction(batch)
import { notifyManager } from '@tanstack/query-core'
import { batch } from 'solid-js'
notifyManager.setBatchNotifyFunction(batch)
为了更好地支持并发功能和转换,我们对 hydration API 进行了一些更改。Hydrate
组件已重命名为 HydrationBoundary,并且已移除 useHydrate hook。
HydrationBoundary 不再 hydration mutations,仅 hydration queries。要 hydration mutations,请使用低级别的 hydrate API 或 persistQueryClient 插件。
最后,作为技术细节,查询 hydration 的时机略有改变。新查询仍在渲染阶段 hydration,以便 SSR 正常工作,但任何已存在于缓存中的查询现在都在 effect 中 hydration(只要其数据比缓存中的数据更新)。如果您只在应用程序开始时进行一次 hydration,这通常不会影响您,但如果您使用 Server Components 并在页面导航时传递更新的数据进行 hydration,您可能会在页面立即重新渲染之前注意到旧数据的闪烁。
最后这个更改在技术上是一个破坏性更改,它被做出是为了避免在页面转换完全提交之前过早地更新*现有*页面上的内容。您无需采取任何行动。
- import { Hydrate } from '@tanstack/react-query' // [!code --]
+ import { HydrationBoundary } from '@tanstack/react-query' // [!code ++]
- <Hydrate state={dehydratedState}> // [!code --]
+ <HydrationBoundary state={dehydratedState}> // [!code ++]
<App />
- </Hydrate> // [!code --]
+ </HydrationBoundary> // [!code ++]
- import { Hydrate } from '@tanstack/react-query' // [!code --]
+ import { HydrationBoundary } from '@tanstack/react-query' // [!code ++]
- <Hydrate state={dehydratedState}> // [!code --]
+ <HydrationBoundary state={dehydratedState}> // [!code ++]
<App />
- </Hydrate> // [!code --]
+ </HydrationBoundary> // [!code ++]
queryClient.getQueryDefaults 现在将合并所有匹配的注册,而不是只返回第一个匹配的注册。
因此,对 queryClient.setQueryDefaults 的调用现在应该按*递增*特异性排序。也就是说,注册应该从最通用键到最不通用键进行。
例如
+ queryClient.setQueryDefaults(['todo'], { // [!code ++]
+ retry: false, // [!code ++]
+ staleTime: 60_000, // [!code ++]
+ }) // [!code ++]
queryClient.setQueryDefaults(['todo', 'detail'], {
+ retry: true, // [!code --]
retryDelay: 1_000,
staleTime: 10_000,
})
- queryClient.setQueryDefaults(['todo'], { // [!code --]
- retry: false, // [!code --]
- staleTime: 60_000, // [!code --]
- }) // [!code --]
+ queryClient.setQueryDefaults(['todo'], { // [!code ++]
+ retry: false, // [!code ++]
+ staleTime: 60_000, // [!code ++]
+ }) // [!code ++]
queryClient.setQueryDefaults(['todo', 'detail'], {
+ retry: true, // [!code --]
retryDelay: 1_000,
staleTime: 10_000,
})
- queryClient.setQueryDefaults(['todo'], { // [!code --]
- retry: false, // [!code --]
- staleTime: 60_000, // [!code --]
- }) // [!code --]
请注意,在此特定示例中,retry: true 已添加到 ['todo', 'detail'] 注册中,以抵消它现在从更通用的注册继承 retry: false 的情况。为保持行为不变而需要进行的具体更改将取决于您的默认设置。
v5 还带来了新功能
我们通过利用 useMutation 返回的 variables,提供了一种新的、简化的执行乐观更新的方法
const queryInfo = useTodos()
const addTodoMutation = useMutation({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
})
if (queryInfo.data) {
return (
<ul>
{queryInfo.data.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
{addTodoMutation.isPending && (
<li key={String(addTodoMutation.submittedAt)} style={{ opacity: 0.5 }}>
{addTodoMutation.variables}
</li>
)}
</ul>
)
}
const queryInfo = useTodos()
const addTodoMutation = useMutation({
mutationFn: (newTodo: string) => axios.post('/api/data', { text: newTodo }),
onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
})
if (queryInfo.data) {
return (
<ul>
{queryInfo.data.items.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
{addTodoMutation.isPending && (
<li key={String(addTodoMutation.submittedAt)} style={{ opacity: 0.5 }}>
{addTodoMutation.variables}
</li>
)}
</ul>
)
}
在这里,我们只是在 mutation 运行时改变 UI 外观,而不是直接将数据写入缓存。当只需要在一个地方显示乐观更新时,这种方法效果最好。有关更多详细信息,请查看 乐观更新文档。
无限查询在需要无限滚动或分页时非常有用。但是,获取的页面越多,消耗的内存就越多,并且由于所有页面都会被顺序重取,这也会减慢查询重取过程。
版本 5 为无限查询提供了一个新的 maxPages 选项,该选项允许开发人员限制存储在查询数据中以及随后重取的页面数量。您可以根据您想要交付的用户体验和重取性能来调整 maxPages 值。
请注意,无限列表必须是双向的,这需要同时定义 getNextPageParam 和 getPreviousPageParam。
无限查询可以像普通查询一样进行预取。默认情况下,只有查询的第一页会被预取并存储在给定的 QueryKey 下。如果您想预取多个页面,可以使用 pages 选项。有关更多信息,请阅读 预取指南。
useQueries
的新 combine 选项有关更多详细信息,请参阅 useQueries 文档。
有关更多详细信息,请参阅 experimental_createPersister 文档。
有关更多详细信息,请参阅 TypeScript 文档。
使用 v5,数据获取的 suspense 最终变得“稳定”。我们添加了专用的 useSuspenseQuery、useSuspenseInfiniteQuery 和 useSuspenseQueries hook。使用这些 hook,data 在类型级别上永远不会是潜在的 undefined
const { data: post } = useSuspenseQuery({
// ^? const post: Post
queryKey: ['post', postId],
queryFn: () => fetchPost(postId),
})
const { data: post } = useSuspenseQuery({
// ^? const post: Post
queryKey: ['post', postId],
queryFn: () => fetchPost(postId),
})
查询 hook 上实验性的 suspense: boolean 标志已移除。
您可以在 suspense 文档 中阅读更多关于它们的信息。