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
不再返回 Promise。true
/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
已从 枚举 更改为 联合类型。以前的 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: {
// query options
},
mutations: {
// mutation options
},
},
})
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// query options
},
mutations: {
// mutation options
},
},
})
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 }))
它已经存在很久了,但最终还是被移除了 :)
新的 QueryClient.prefetchQuery() 函数是异步的,但不返回查询数据。如果需要数据,请使用新的 QueryClient.fetchQuery() 函数。
// Prefetch a query:
await queryClient.prefetchQuery('posts', fetchPosts)
// Fetch a query:
try {
const data = await queryClient.fetchQuery('posts', fetchPosts)
} catch (error) {
// Error handling
}
// Prefetch a query:
await queryClient.prefetchQuery('posts', fetchPosts)
// Fetch a query:
try {
const data = await queryClient.fetchQuery('posts', fetchPosts)
} catch (error) {
// Error handling
}
它们共同提供了与以前相同的体验,但增加了控制权,可以选择要重置的组件树。欲了解更多信息,请参阅。
现在应该使用 QueryCache.find() 从缓存中查找单个查询。
现在应该使用 QueryCache.findAll() 从缓存中查找多个查询。
请注意,它现在是一个函数而不是一个属性。
useQueryCache
钩子已被 useQueryClient
钩子替换。它返回其组件树的 queryClient,除了重命名之外,应该不需要太多修改。
现在建议使用内联函数将参数传递给查询函数。
// Old
useQuery(['post', id], (_key, id) => fetchPost(id))
// New
useQuery(['post', id], () => fetchPost(id))
// Old
useQuery(['post', id], (_key, id) => fetchPost(id))
// New
useQuery(['post', id], () => fetchPost(id))
如果您仍然坚持不使用内联函数,可以使用新传递的 QueryFunctionContext。
useQuery(['post', id], (context) => fetchPost(context.queryKey[1]))
useQuery(['post', id], (context) => fetchPost(context.queryKey[1]))
它们以前是作为查询函数中最后一个查询键参数添加的,但这对于某些模式来说很难。
// Old
useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam))
// New
useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPosts(pageParam))
// Old
useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam))
// New
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 时那种温暖模糊的感觉,但它们并没有持续多久。现在修改的返回是一个单一的对象。
// Old:
const [mutate, { status, reset }] = useMutation()
// New:
const { mutate, status, reset } = useMutation()
// Old:
const [mutate, { status, reset }] = useMutation()
// New:
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')
},
})
在使用 async/await 时可以使用 mutateAsync 函数。
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')
}
// Old:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
config: { staleTime: Infinity },
})
// New:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
staleTime: Infinity,
})
// Old:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
config: { staleTime: Infinity },
})
// New:
useQuery({
queryKey: 'posts',
queryFn: fetchPosts,
staleTime: Infinity,
})
现在,enabled 查询选项只有当值为 false 时才会禁用查询。如果需要,可以使用 !!userId 或 Boolean(userId) 来强制转换值,如果传递非布尔值,则会抛出便捷错误。
initialStale 查询选项已移除,初始数据现在被视为常规数据。这意味着如果提供了 initialData,查询将默认在挂载时重新获取。如果您不想立即重新获取,可以定义一个 staleTime。
QueryOptions.forceFetchOnMount
选项已替换为 refetchOnMount: 'always'
。说实话,我们累积了太多 refetchOn____ 选项,所以这应该能清理一下。
QueryOptions.refetchOnMount
选项现在只适用于其父组件,而不是所有查询观察者。当 refetchOnMount 设置为 false 时,会阻止任何其他组件在挂载时重新获取。在版本 3 中,只有设置了该选项的组件才不会在挂载时重新获取。
QueryOptions.queryFnParamsFilter
已被移除,取而代之的是新的 QueryFunctionContext
对象。queryFnParamsFilter 选项已移除,因为查询函数现在获取 QueryFunctionContext 对象而不是查询键。
参数仍然可以在查询函数本身中进行过滤,因为 QueryFunctionContext 也包含查询键。
QueryOptions.notifyOnStatusChange
选项已被新的 notifyOnChangeProps
和 notifyOnChangePropsExclusions
选项取代。借助这些新选项,可以精细配置组件何时重新渲染。
仅当 data 或 error 属性更改时才重新渲染。
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangeProps: ['data', 'error'],
})
return <div>Username: {data.username}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangeProps: ['data', 'error'],
})
return <div>Username: {data.username}</div>
}
当 isStale 属性更改时,阻止重新渲染。
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangePropsExclusions: ['isStale'],
})
return <div>Username: {data.username}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
notifyOnChangePropsExclusions: ['isStale'],
})
return <div>Username: {data.username}</div>
}
QueryResult.clear()
函数已重命名为 QueryResult.remove()
。虽然它被称为 clear,但它实际上只是将查询从缓存中移除。现在名称与功能匹配。
QueryResult.updatedAt
属性已拆分为 QueryResult.dataUpdatedAt
和 QueryResult.errorUpdatedAt
属性。由于数据和错误可以同时存在,updatedAt 属性已拆分为 dataUpdatedAt 和 errorUpdatedAt。
import { setLogger } from 'react-query'
// Log with Sentry
setLogger({
error: (error) => {
Sentry.captureException(error)
},
})
// Log with Winston
setLogger(winston.createLogger())
import { setLogger } from 'react-query'
// Log with Sentry
setLogger({
error: (error) => {
Sentry.captureException(error)
},
})
// Log with 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 Native 中使用 React Query 时,这会自动完成。
因此,如果您之前根据 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>Username: {data}</div>
}
import { useQuery } from 'react-query'
function User() {
const { data } = useQuery(['user'], fetchUser, {
select: (user) => user.username,
})
return <div>Username: {data}</div>
}
将 notifyOnChangeProps 选项设置为 ['data', 'error'],仅在选定数据更改时重新渲染。
希望能在循环中运行 useQuery 吗?Hook 规则不允许,但有了新的 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'
devtools 现在包含在 react-query 包本身中,导入路径为 react-query/devtools。只需将 react-query-devtools 导入替换为 react-query/devtools。