文章目录
- react query 学习笔记
- 查询客户端 QueryClient
- 获取查询客户端 useQueryClient
- 异步重新请求数据 queryClient.fetchQuery / queryClient.refetchQueries
- 设置查询失效并重新获取活动查询 ueryClient.invalidateQueries
- 查询 Queries
- useQuery查询配置对象
- 查询的键值 Query Keys
- 查询函数Query Functions
- 初始化的查询数据(查询数据占位符) initialData 与 placeholderData
- useQuery的返回值
- 修改 useMutation
- useMutation配置对象
- useMutation的返回值对象
- 触发mutationFn请求的属性mutate与mutateAsync
- 过滤器 Filters
- 查询过滤器 QueryFilters
react query 学习笔记
react-query
只会对当前react
中正在挂载的组件进行重新获取操作,如果组件被卸载(比如离开了当前页面组件等),并不会执行上述操作。而且当组件被卸载超过5
分钟后,缓存将会被自动清除。
数据重新获取(请求重新发送的时机)
需要同时满足以下两个请求
1.目前缓存内的数据是过期的
2.以下三种情况之一 ①带有useQueryhooks
的组件挂载时②当用户将浏览器重新聚焦,切换到前台时③网络从离线到上线
概念
- 非活动查询:一旦没有观察者注册,该查询(使用query key识别一个查询)就会转换到非活动状态。
useQuery
调用注册一个查询观察者,观察到依赖数据变化就重新请求数据?
版本是
react query 5
, 只记录工作中使用到的主要功能,在使用中会不断更新,关于返回值等具体参数可以通过打印返回值查看。
阅读文章:https://juejin.cn/post/7202945024748912699
react query文档:https://cangsdarm.github.io/react-query-web-i18n/react/
react query文档的简单翻译版:https://juejin.cn/post/7123119750523125796
查询客户端 QueryClient
作用:①管理缓存数据,
import { QueryClient } from '@tanstack/react-query'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
},
},
})
需要将查询客户端queryClient
提供给全局App
import {
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
// 创建一个 client
const queryClient = new QueryClient();
function App() {
return (
// 提供 client 至 App
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
);
}
获取查询客户端 useQueryClient
在项目的组件中,也可以调用queryClient
,使用useQueryClient
钩子,就可以获取全局的查询客户端queryClient
。
import { useQueryClient } from '@tanstack/react-query'
const queryClient = useQueryClient();
queryClient对象的属性
用法 | 语法 | 描述 | 同步或者异步 | 返回值 |
---|---|---|---|---|
获取缓存数据 | getQueryData(queryKey?: QueryKey,filters?: QueryFilters) |
获取现有查询的缓存数据,如果查询不存在,则返回undefined
|
同步 | 缓存的数据 |
获取请求的状态 | getQueryState(queryKey?: QueryKey,filters?: QueryFilters) |
获取现有查询的状态,如果查询不存在,则返回undefined
|
同步 | 状态对象dataUpdatedAt: number 属性:查询最近一次返回status 为"success" 的时间戳 |
异步重新请求数据 queryClient.fetchQuery / queryClient.refetchQueries
-
queryClient.fetchQuery({uery Keys,Query Function,other})
:异步请求数据,类似useQuery
,可用于提取和缓存查询。
在特殊场景下(如:强制触发查询等)可以使用queryClient.fetchQuery()
请求数据。
如果在缓存中有对应的数据(通过查询键匹配)且未过期,可以无需请求直接使用缓存数据。
如果没有缓存或缓存已经过期,那么react-query
会重新请求并且缓存数据。 -
queryClient.refetchQueries(配置参数)
:所有匹配的查询请求(查询键值是模糊匹配)都重新发送,该函数的配置参数是查询过滤器。// refetch all queries: await queryClient.refetchQueries(); // refetch all stale queries: await queryClient.refetchQueries({ stale: true }); // refetch all active queries partially matching a query key: await queryClient.refetchQueries(["posts"], { type: "active" }); // refetch all active queries exactly matching a query key: await queryClient.refetchQueries(["posts", 1], { type: "active", exact: true });
设置查询失效并重新获取活动查询 ueryClient.invalidateQueries
- 如果您不想重新获取活动查询,而只是将其标记为无效,则可以使用该refetchType: 'none’选项。
- 如果您还希望重新获取非活动查询,请使用该refetchTye: 'all’选项
查询 Queries
ReactQuery
会在全局维护一个服务端状态树,根据 Query key
去查找状态树中是否有可用的数据,如果有则直接返回,否则则会发起请求,并将请求结果以 Query key
为主键存储到状态树中。
订阅一个查询useQuery({配置对象})
,通常包含两个参数:
- 唯一标识这个请求的
Query key
:ReactQuery
的缓存策略是基于这个key
来实现的,key
变化缓存失效,请求重新发送。 - 一个真正执行请求并返回数据的异步方法
查询的三个钩子
-
useQuery
:发起单个请求 -
useQueries
:发起多个请求 -
useInfiniteQuery
:无限查询
const {
data,
dataUpdatedAt,
error,
errorUpdatedAt,
failureCount,
failureReason,
fetchStatus,
isError,
isFetched,
isFetchedAfterMount,
isFetching,
isInitialLoading,
isLoading,
isLoadingError,
isPaused,
isPending,
isPlaceholderData,
isRefetchError,
isRefetching,
isStale,
isSuccess,
refetch,
status,
} = useQuery(
{
queryKey,
queryFn,
gcTime,
enabled,
networkMode,
initialData,
initialDataUpdatedAt,
meta,
notifyOnChangeProps,
placeholderData,
queryKeyHashFn,
refetchInterval,
refetchIntervalInBackground,
refetchOnMount,
refetchOnReconnect,
refetchOnWindowFocus,
retry,
retryOnMount,
retryDelay,
select,
staleTime,
structuralSharing,
throwOnError,
},
queryClient,
)
useQuery查询配置对象
键 | 描述 |
---|---|
queryKey | 查询请求的key |
queryFn | 查询请求的请求函数 |
enabled: boolean | 查询是否自动运行,是否可用,为false 不会发起请求。可以控制查询请求运行的时机(比如请求参数不为空时才发送查询请求 param!==null ) 也可以使用 useQuery 返回的refetch 触发 |
staleTime: number | Infinit | 数据缓存的时长,单位是毫秒,默认为0。在时间范围内,再次发送请求时,直接使用缓存数据。 |
查询的键值 Query Keys
语法:queryKey:数组
要求数组可序列化,并且数组唯一。
作用:React Query
在内部基于查询键值来管理查询缓存,可以理解为依赖,如果依赖变化那么会重新发送查询请求。
import { useQuery} from "@tanstack/react-query";
function Todos({ todoId }) {{
const result = useQuery({
queryKey: ['todos', todoId],
queryFn: () => fetchTodoById(todoId),
});
}
注意点:查询键值序列化后,相同的会被去重。数组项的顺序影响序列化后的值,对象的key的顺序不影响序列化后的值。
// 不管对象中键值的顺序如何,以下所有查询都被认为是相等的:
useQuery({ queryKey: ['todos', { status, page }], ... });
useQuery({ queryKey: ['todos', { page, status }], ...});
useQuery({ queryKey: ['todos', { page, status, other: undefined }], ... });
//以下查询键值不相等
useQuery({ queryKey: ['todos', status, page], ... });
useQuery({ queryKey: ['todos', page, status], ...});
useQuery({ queryKey: ['todos', undefined, page, status], ...});
查询函数Query Functions
查询函数的参数
每一个查询函数的参数都是QueryFunctionContext
对象,该对象中包含
-
queryKey: QueryKey
: 查询键值 -
pageParam: unknown | undefined
:只会在无限查询场景传递,为查询当前页所使用的参数 -
signal?: AbortSignal
:可以用来做查询取消 -
meta?: Record<string, unknown>
:一个可选字段,可以填写任意关于该查询的额外信息
function Todos({ status, page }) {
const result = useQuery({
queryKey: ["todos", { status, page }],
queryFn: fetchTodoList,
});
}
// 在查询函数中访问键值,状态和页面变量!
function fetchTodoList({ queryKey }) {
const [_key, { status, page }] = queryKey;
return new Promise();
}
查询函数的返回值
查询函数要求是返回Promise
的函数(异步同步都可以),其该Promise
最终状态会变成resolved
或rejected
。
初始化的查询数据(查询数据占位符) initialData 与 placeholderData
使用场景:在应用的其他地方已经获取到了查询的初始数据或者自定义一个初始数据
属性 | 保留在缓存 | 描述 |
---|---|---|
initialData |
√ 保留在缓存中,因此不建议为此选项提供占位符,部分或不完整的数据 |
初始化数据,一般用于已经有完整的数据但是不确定数据是否已经变更的场景 initialData 受staleTime影响,如果初始化数据没过时,就会一直使用初始化数据, 否则重新发起请求 |
placeholderData |
× 数据没有被持久化到缓存中,更倾向于预览的作用 |
在还未第一次请求到数据之前的这段时间,如果获取该查询的data ,则会获取到placeholderData 属性的值 一旦查询从后端获取到了数据,则placeholderData属性失效 |
placeholderData理解与使用
1.placeholderData
类似于解构,如果解构项的值为undefined
,将会采用设置的默认值
{
const issuesQuery = useQuery(
queryKey:["issues"],
queryFn:fetch,
placeholderData: [],
}
)
const { data = [] } = useQuery(
{
queryKey:["issues"],
queryFn:fetch,
}
)
2.当需要为用户提供假数据时,就可以使用placeholderData
,比如在获取用户数据时,为了UI不那么难看,可以先默认展示一个占位头像。
3.如某个依赖查询,依赖了该数据需要使用isPlaceholderData
属性来判断当前数据是否真实.
// 请求1
const userQuery = useQuery(
{
queryKey:["user"],
queryFn:fetchUser,
}
)
// 依赖请求1返回值的请求2
const userIssues = useQuery(
{
queryKey:["userIssues", userQuery.data.login],
queryFn:fetchUserIssues,
enabled: !userQuery.isPlaceholderData
&& userQuery.data?.login,
}
)
initialData理解与使用
1.使用案例
// 将立即显示 initialTodos,但在挂载后也将立即重新获取todos, initialData立即过期
const result = useQuery({
queryKey: ["todos"],
queryFn: () => fetch("/todos"),
initialData: initialTodos,
});
// 使用之前的缓存信息:将 `todos` 查询中的某个 todo 用作此 todos 查询的初始数据
const result = useQuery({
queryKey: ["todo", todoId],
queryFn: () => fetch("/todos"),
initialData: () => {
return queryClient.getQueryData(["todos"])?.find((d) => d.id === todoId);
},
});
2.精准控制过期时间,过期时间 = initialDataUpdatedAt
+ staleTime
initialDataUpdatedAt
:表示初始数据上一次更新的时间,可以与getQueryState
钩子函数、staleTime
配置属性精准控制initialData
过期时间。initialDataUpdatedAt
类型为Number
类型的 JS
时间戳(以毫秒为单位,如Date.now())
const issueDetailQuery = useQuery(
{
queryKey:["issue", repo, owner, issueNumber],
queryFn:fetchIssue,
staleTime: 1000 * 60,
initialData: () => { // 初始数据的过期时间为getQueryData上一次刷新的时间+staleTime的时间
const issues = queryClient.getQueryData(["issues", repo, owner])
if (!issues) return undefined;
const issue = issues.find(issue => issue.number === issueNumber)
return issue;
},
initialDataUpdatedAt: () => { // 初始数据issue最后一次新时间dataUpdatedAt
const {dataUpdatedAt} = queryClient.getQueryState(["issues", repo, owner])
return dataUpdatedAt;
}
},
)
useQuery的返回值
useQuery
返回值为包含所有关于该查询信息的对象
status
告诉我们有关data
的状态:有或者没有?
-
status
:查询的状态,有正在加载、失败、成功三个状态 -
isLoading
或者status === 'loading'
:查询暂时还没有数据 -
isError
或者status === 'error'
:查询出错 -
isSuccess
或者status === 'success'
查询成功,并且数据可用
以下两个状态是获取查询函数queryFn
的返回值(Promise
的返回值而不是一个新的Promise
)
-
error
:如果查询处于isError
状态,则可以通过error
属性获取该错误 -
data
:如果查询处于isSuccess
状态,则可以通过data
属性获得数据
fetchStatus
告诉我们有关queryFn
的状态:在执行还是没在执行?
-
fetchStatus === 'fetching'
正在查询中 -
fetchStatus === 'paused'
查询想要获取,但它被暂停了 -
fetchStatus === 'idle'
该查询处于闲置状态
为什么需要两个键来表示对象?
后台刷新和数据过期重试可以让一个请求处于多种情况。
一个state='success'
的查询通常处于fetchStatus='idle'
状态。但如果同时后台重新获取动作,该查询也可能为fetchStatus='fetching'
状态。
一个没有数据的查询通常处于status='loading'
状态和fetchStatus='loading'
状态。如果同时无网络连接,该请求也可能为fetchStatus='paused'
状态。
修改 useMutation
说明:react-query
中使用useMutation
向后端发送创建/更新/删除
操作mutationFn
的调用时机:useMutation
钩子不会在组件加载时就直接请求,需要手动调用mutate
方法并传入请求参数才会生效。
const {
data,
error,
isError,
isIdle,
isPending,
isPaused,
isSuccess,
failureCount,
failureReason,
mutate,
mutateAsync,
reset,
status,
submittedAt,
variables,
} = useMutation({
mutationFn,
gcTime,
mutationKey,
networkMode,
onError,
onMutate,
onSettled,
onSuccess,
retry,
retryDelay,
throwOnError,
meta,
})
使用案例:添加一个新todo
function App() {
const mutation = useMutation({
mutationFn: (newTodo) => {
return axios.post("/todos", newTodo);
},
});
return (
<div>
{mutation.isLoading ? (
"Adding todo..."
) : (
<>
{mutation.isError ? (
<div>An error occurred: {mutation.error.message}</div>
) : null}
{mutation.isSuccess ? <div>Todo added!</div> : null}
<button
onClick={() => {
mutation.mutate({ id: new Date(), title: "Do Laundry" });
}}
>
Create Todo
</button>
</>
)}
</div>
);
}
useMutation配置对象
属性 | 描述 |
---|---|
mutationFn: (variables: TVariables) => Promise | 必选:执行异步任务并返回承诺的函数,参数variables 是一个mutate 传递给mutationFn 的对象 |
useMutation的返回值对象
触发mutationFn请求的属性mutate与mutateAsync
-
useMutation返回值对象.mutate
:同步触发 -
useMutation返回值对象.mutateAsync
:异步触发
mutate(variables, {
onError,
onSettled,
onSuccess,
})
过滤器 Filters
某些方法可以接受查询过滤QueryFilters
或者修改过滤 MutationFilters
对象。
// 取消所有查询
await queryClient.cancelQueries();
// 删除所有以`posts`开头的键值的非活跃的查询
queryClient.removeQueries({ queryKey: ["posts"], type: "inactive" });
// 重新获取所有活跃的查询
await queryClient.refetchQueries({ type: 'active' })
// 重新获取键中以`posts`开头的所有活跃的查询
await queryClient.refetchQueries({ queryKey: ['posts'], type: 'active' })
查询过滤器 QueryFilters
属性名 | 描述 |
---|---|
queryKey?: QueryKey | 设置此属性以定义要匹配的查询键值,默认匹配的规则是以queryKey开头的查询(模糊查询)。 |
exact?: boolean | 是否关闭模糊查询,默认是false
|
type?: ‘active’ | ‘inactive’ | ‘all’ | 默认为allactive 表示匹配活跃查询inactive 表示匹配非活跃的查询 |
stale?: boolean |
true 表示当前过时(staled )的false 表示匹配当前没过时(fresh )的 |
fetchStatus?: FetchStatus |
fetching 匹配当前正在获取的paused 匹配当前想要获取但被暂停了的idle 配当前未在获取的 |