QueryCache 已拆分为 QueryClient 和更低级的 QueryCache 和 MutationCache 实例。ReactQueryConfigProvider 和 ReactQueryCacheProvider 都已被 QueryClientProvider 取代QueryCache 消失了。这次是真的!makeQueryCache 工具已被删除。QueryCache.prefetchQuery() 已移至 QueryClient.prefetchQuery()ReactQueryErrorResetBoundary 和 QueryCache.resetErrorBoundaries() 已被 QueryErrorResetBoundary 和 useQueryErrorResetBoundary() 取代。QueryCache.getQuery() 已被 QueryCache.find() 取代。QueryCache.getQueries() 已移至 QueryCache.findAll()。QueryCache.isFetching 已移至 QueryClient.isFetching()。useQueryCache 钩子已被 useQueryClient 钩子取代。QueryFunctionContext.pageParam 传递keepPreviousData 选项mutation.mutate 不再返回 Promisetrue/false)QueryOptions.forceFetchOnMount 选项已被 refetchOnMount: 'always' 取代QueryOptions.refetchOnMount 选项现在仅适用于其父组件,而不是所有查询观察者QueryOptions.queryFnParamsFilter 已被删除,取而代之的是新的 QueryFunctionContext 对象。QueryOptions.notifyOnStatusChange 选项已被新的 notifyOnChangeProps 和 notifyOnChangePropsExclusions 选项取代。QueryResult.clear() 函数已重命名为 QueryResult.remove()QueryResult.updatedAt 属性已拆分为 QueryResult.dataUpdatedAt 和 QueryResult.errorUpdatedAt 属性setConsole() 已被新的 setLogger() 函数取代QueryStatus 已从枚举更改为联合类型useQueries() 钩子,用于可变长度并行查询执行React Query 的先前版本非常棒,为库带来了许多令人惊叹的新功能、更多魔力以及整体更好的体验。它们也带来了大规模的采用,同样也为库带来了大量的改进(问题/贡献),并揭示了一些需要进一步完善以使库变得更好的地方。v3 包含了这种完善。
QueryCache 包含所有查询,MutationCache 包含所有变更,QueryClient 可用于设置配置并与它们交互。
这有一些好处:
创建 new QueryClient() 时,如果您不提供 QueryCache 和 MutationCache,它们会自动为您创建。
import { QueryClient } from 'react-query'
const queryClient = new QueryClient()
import { QueryClient } from 'react-query'
const queryClient = new QueryClient()
现在可以在 QueryClient 中指定查询和变更的默认选项:
请注意,现在是 defaultOptions 而不是 defaultConfig
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// 查询选项
},
mutations: {
// 变更选项
},
},
})
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// 查询选项
},
mutations: {
// 变更选项
},
},
})
QueryClientProvider 组件现在用于将 QueryClient 连接到您的应用程序:
import { QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient()
function App() {
return <QueryClientProvider client={queryClient}>...</QueryClientProvider>
}
import { QueryClient, QueryClientProvider } from 'react-query'
const queryClient = new QueryClient()
function App() {
return <QueryClientProvider client={queryClient}>...</QueryClientProvider>
}
正如之前弃用通知中所述,主包中不再创建或导出默认的 QueryCache。您必须通过 new QueryClient() 或 new QueryCache()(然后可以将其传递给 new QueryClient({ queryCache }))创建自己的 QueryCache
它已经存在很长时间了,但最终还是消失了 :)
新的 QueryClient.prefetchQuery() 函数是异步的,但不会从查询中返回数据。如果您需要数据,请使用新的 QueryClient.fetchQuery() 函数
// 预取查询:
await queryClient.prefetchQuery('posts', fetchPosts)
// 获取查询:
try {
const data = await queryClient.fetchQuery('posts', fetchPosts)
} catch (error) {
// 错误处理
}
// 预取查询:
await queryClient.prefetchQuery('posts', fetchPosts)
// 获取查询:
try {
const data = await queryClient.fetchQuery('posts', fetchPosts)
} catch (error) {
// 错误处理
}
它们共同提供了与以前相同的体验,但增加了选择要重置哪些组件树的控制权。有关更多信息,请参阅:
现在应使用 QueryCache.find() 从缓存中查找单个查询
现在应使用 QueryCache.findAll() 从缓存中查找多个查询
请注意,它现在是一个函数而不是一个属性
它为其组件树返回提供的 queryClient,除了重命名之外,不需要太多调整。
内联函数现在是向查询函数传递参数的建议方法:
// 旧版
useQuery(['post', id], (_key, id) => fetchPost(id))
// 新版
useQuery(['post', id], () => fetchPost(id))
// 旧版
useQuery(['post', id], (_key, id) => fetchPost(id))
// 新版
useQuery(['post', id], () => fetchPost(id))
如果您仍然坚持不使用内联函数,可以使用新传递的 QueryFunctionContext:
useQuery(['post', id], (context) => fetchPost(context.queryKey[1]))
useQuery(['post', id], (context) => fetchPost(context.queryKey[1]))
它们以前作为查询函数中的最后一个查询键参数添加,但这对于某些模式来说被证明是困难的
// 旧版
useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam))
// 新版
useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPosts(pageParam))
// 旧版
useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam))
// 新版
useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPosts(pageParam))
新的 keepPreviousData 选项可用于 useQuery 和 useInfiniteQuery,并且会对您的数据产生相同的“滞后���效果:
import { useQuery } from 'react-query'
function Page({ page }) {
const { data } = useQuery(['page', page], fetchPage, {
keepPreviousData: true,
})
}
import { useQuery } from 'react-query'
function Page({ page }) {
const { data } = useQuery(['page', page], fetchPage, {
keepPreviousData: true,
})
}
useInfiniteQuery() 接口已更改以完全支持双向无限列表。
单向:
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
)
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
)
双向:
const {
data,
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
} = useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,
},
)
const {
data,
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
isFetchingNextPage,
isFetchingPreviousPage,
} = useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,
},
)
单向反转:
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
select: (data) => ({
pages: [...data.pages].reverse(),
pageParams: [...data.pageParams].reverse(),
}),
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
)
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
useInfiniteQuery(
'projects',
({ pageParam = 0 }) => fetchProjects(pageParam),
{
select: (data) => ({
pages: [...data.pages].reverse(),
pageParams: [...data.pageParams].reverse(),
}),
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
},
)
这使得更容易操作数据和页面参数,例如,删除第一页数据及其参数:
queryClient.setQueryData(['projects'], (data) => ({
pages: data.pages.slice(1),
pageParams: data.pageParams.slice(1),
}))
queryClient.setQueryData(['projects'], (data) => ({
pages: data.pages.slice(1),
pageParams: data.pageParams.slice(1),
}))
虽然旧的方式让我们感受到了第一次发现 useState 时的温暖模糊的感觉,但它们并没有持续多久。现在变更返回的是一个单一对象。
// 旧版:
const [mutate, { status, reset }] = useMutation()
// 新版:
const { mutate, status, reset } = useMutation()
// 旧版:
const [mutate, { status, reset }] = useMutation()
// 新版:
const { mutate, status, reset } = useMutation()
我们收到了很多关于此行为的问题,因为用户希望 Promise 的行为与常规 Promise 类似。
因此,mutate 函数现在拆分为 mutate 和 mutateAsync 函数。
mutate 函数可用于使用回调时:
const { mutate } = useMutation({ mutationFn: addTodo })
mutate('todo', {
onSuccess: (data) => {
console.log(data)
},
onError: (error) => {
console.error(error)
},
onSettled: () => {
console.log('settled')
},
})
const { mutate } = useMutation({ mutationFn: addTodo })
mutate('todo', {
onSuccess: (data) => {
console.log(data)
},
onError: (error) => {
console.error(error)
},
onSettled: () => {
console.log('settled')
},
})
mutateAsync 函数可用于使用 async/await 时:
const { mutateAsync } = useMutation({ mutationFn: addTodo })
try {
const data = await mutateAsync('todo')
console.log(data)
} catch (error) {
console.error(error)
} finally {
console.log('settled')
}
const { mutateAsync } = useMutation({ mutationFn: addTodo })
try {
const data = await mutateAsync('todo')
console.log(data)
} catch (error) {
console.error(error)
} finally {
console.log('settled')
}
// 旧版:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
config: { staleTime: Infinity },
})
// 新版:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
staleTime: Infinity,
})
// 旧版:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
config: { staleTime: Infinity },
})
// 新版:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
staleTime: Infinity,
})
enabled 查询选项现在仅在值为 false 时禁用查询。 如果需要,可以使用 !!userId 或 Boolean(userId) 转换值,如果传递了非布尔值,则会抛出一个方便的错误。
initialStale 查询选项已被删除,初始数据现在被视为常规数据。 这意味着如果提供了 initialData,则查询默认情况下会在挂载时重新获取。 如果您不想立即重新获取,可以定义一个 staleTime。
老实说,我们积累了太多 refetchOn____ 选项,所以这应���可以清理一下。
当 refetchOnMount 设置为 false 时,任何其他组件都会阻止在挂载时重新获取。 在版本 3 中,只有设置了该选项的组件才不会在挂载时重新获取。
queryFnParamsFilter 选项已被删除,因为查询函数现在获取的是 QueryFunctionContext 对象而不是查询键。
参数仍然可以在查询函数本身内部进行过滤,因为 QueryFunctionContext 也包含查询键。
通过这些新选项,可以在更精细的级别上配置组件何时应重新渲染。
仅在 data 或 error 属性更改时重新渲染:
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangeProps: ['data', 'error'],
})
return <div>用户名:{data.username}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangeProps: ['data', 'error'],
})
return <div>用户名:{data.username}</div>
}
当 isStale 属性更改时阻止重新渲染:
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangePropsExclusions: ['isStale'],
})
return <div>用户名:{data.username}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangePropsExclusions: ['isStale'],
})
return <div>用户名:{data.username}</div>
}
虽然它被称为 clear,但它实际上只是从缓存中删除了查询。名称现在与功能匹配。
由于数据和错误可以同时存在,因此 updatedAt 属性已拆分为 dataUpdatedAt 和 errorUpdatedAt。
import { setLogger } from 'react-query'
// 使用 Sentry 记录
setLogger({
error: (error) => {
Sentry.captureException(error)
},
})
// 使用 Winston 记录
setLogger(winston.createLogger())
import { setLogger } from 'react-query'
// 使用 Sentry 记录
setLogger({
error: (error) => {
Sentry.captureException(error)
},
})
// 使用 Winston 记录
setLogger(winston.createLogger())
为了防止在查询失败时在 React Native 中显示错误屏幕,需要手动更改控制台:
import { setConsole } from 'react-query'
setConsole({
log: console.log,
warn: console.warn,
error: console.warn,
})
import { setConsole } from 'react-query'
setConsole({
log: console.log,
warn: console.warn,
error: console.warn,
})
在版本 3 中,当 React Query 在 React Native 中使用时,这是自动完成的。
因此,如果您以前根据 QueryStatus 枚举属性检查查询或变更的状态属性,现在需要根据枚举先前为每个属性保留的字符串字面量进行检查。
因此,您必须将枚举属性更改为其等效的字符串字面量,如下所示:
以下是您需要进行的更改示例:
- import { useQuery, QueryStatus } from 'react-query'; // [!code --]
+ import { useQuery } from 'react-query'; // [!code ++]
const { data, status } = useQuery(['post', id], () => fetchPost(id))
- if (status === QueryStatus.Loading) { // [!code --]
+ if (status === 'loading') { // [!code ++]
...
}
- if (status === QueryStatus.Error) { // [!code --]
+ if (status === 'error') { // [!code ++]
...
}
- import { useQuery, QueryStatus } from 'react-query'; // [!code --]
+ import { useQuery } from 'react-query'; // [!code ++]
const { data, status } = useQuery(['post', id], () => fetchPost(id))
- if (status === QueryStatus.Loading) { // [!code --]
+ if (status === 'loading') { // [!code ++]
...
}
- if (status === QueryStatus.Error) { // [!code --]
+ if (status === 'error') { // [!code ++]
...
}
useQuery 和 useInfiniteQuery 钩子现在有一个 select 选项,用于选择或转换查询结果的某些部分。
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
select: (user) => user.username,
})
return <div>用户名:{data}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
select: (user) => user.username,
})
return <div>用户名:{data}</div>
}
将 notifyOnChangeProps 选项设置为 ['data', 'error'] 以仅在所选数据更改时重新渲染。
希望您可以在循环中运行 useQuery 吗?钩子规则不允许,但使用新的 useQueries() 钩子,您可以!
import { useQueries } from 'react-query'
function Overview() {
const results = useQueries([
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
return (
<ul>
{results.map(({ data }) => data && <li key={data.id}>{data.title})</li>)}
</ul>
)
}
import { useQueries } from 'react-query'
function Overview() {
const results = useQueries([
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
return (
<ul>
{results.map(({ data }) => data && <li key={data.id}>{data.title})</li>)}
</ul>
)
}
默认情况下,React Query 不会因错误重试变更,但可以使用 retry 选项:
const mutation = useMutation({
mutationFn: addTodo,
retry: 3,
})
const mutation = useMutation({
mutationFn: addTodo,
retry: 3,
})
如果由于设备离线而导致变更失败,则在设备重新连接时将以相同的顺序重试它们。
现在可以将变更持久化到存储并在稍后恢复。有关更多信息,请参见变更文档。
QueryObserver 可用于创建和/或监视查询:
const observer = new QueryObserver(queryClient, { queryKey: 'posts' })
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
const observer = new QueryObserver(queryClient, { queryKey: 'posts' })
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
InfiniteQueryObserver 可用于创建和/或监视无限查询:
const observer = new InfiniteQueryObserver(queryClient, {
queryKey: 'posts',
queryFn: fetchPosts,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
})
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
const observer = new InfiniteQueryObserver(queryClient, {
queryKey: 'posts',
queryFn: fetchPosts,
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
})
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
QueriesObserver 可用于创建和/或监视多个查询:
const observer = new QueriesObserver(queryClient, [
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
const observer = new QueriesObserver(queryClient, [
{ queryKey: ['post', 1], queryFn: fetchPost },
{ queryKey: ['post', 2], queryFn: fetchPost },
])
const unsubscribe = observer.subscribe((result) => {
console.log(result)
unsubscribe()
})
QueryClient.setQueryDefaults() 方法可用于设置特定查询的默认选项:
queryClient.setQueryDefaults(['posts'], { queryFn: fetchPosts })
function Component() {
const { data } = useQuery(['posts'])
}
queryClient.setQueryDefaults(['posts'], { queryFn: fetchPosts })
function Component() {
const { data } = useQuery(['posts'])
}
QueryClient.setMutationDefaults() 方法可用于设置特定变更的默认选项:
queryClient.setMutationDefaults(['addPost'], { mutationFn: addPost })
function Component() {
const { mutate } = useMutation({ mutationKey: ['addPost'] })
}
queryClient.setMutationDefaults(['addPost'], { mutationFn: addPost })
function Component() {
const { mutate } = useMutation({ mutationKey: ['addPost'] })
}
useIsFetching() 钩子现在接受过滤器,例如,可用于仅为某些类型的查询显示微调器:
const fetches = useIsFetching({ queryKey: ['posts'] })
const fetches = useIsFetching({ queryKey: ['posts'] })
React Query 的核心现在与 React 完全分离,这意味着它也可以独立使用或在其他框架中使用。使用 react-query/core 入口点仅导入核心功能:
import { QueryClient } from 'react-query/core'
import { QueryClient } from 'react-query/core'
开发者工具现在包含在 react-query 包本身的 react-query/devtools 导入下。只需将 react-query-devtools 导入替换为 react-query/devtools 即可