文章目录
前言
最近团队中用了react-query,了解了一下之后确实挺好玩的,简单记录一下
一、react-query能干嘛?
在前端平时的开发中,可以把我们需要维护的状态分为两类:
- 用户交互的中间状态。如组件的状态,isLoading, isOpen
- 服务端状态。通过请求得到并在前端维护
react-query能够更好的帮助我们处理第二类状态。服务端状态通常会存在组件中,如果要复用,要存在全局状态如redux中。但这样有两个问题:
- 需要重复处理请求中间状态,比如loading,error的判断,每次都要写
- 不同于交互的中间状态,服务端状态更应被归类为「缓存」,对于不同组件要共享的缓存,还要考虑「缓存」的失效和更新
而react-query可以无痛的完全替我们处理这些问题:
- 在hooks内部封装loading,error等方便直接使用
- 多个组件请求同一个query时只发出一个请求
- 缓存数据失效/更新策略(判断缓存合适失效,失效后自动请求数据)
二、常见用例
1.引入库
代码如下(示例):
import { useQuery } from 'react-query';
2.请求useQuery
代码如下(示例):
// 通过useQuery请求数据,hook返回的data和isLoding都可以直接使用
// 除了data,isLoading,还有其他返回如 status,error,isFetching等等
const { data, isLoading } = useQuery(
// useQuery的第一个参数:查询的唯一key,要求必须是可序列化的
['statData', { date, group }],
// useQuery的第二个参数:查询函数,要求必须是返回Promise的函数
// 在查询函数中,也可以提取到key中的参数,比如
// const [_key, { date, department }] = queryKey;
async () => {
const { data: dataObj } = await fetchStatData({
year: date.getFullYear(),
month: date.getMonth(),
deptId: group ? group.id : '',
});
return dataObj;
},
// useQuery的第三个参数:配置对象config
{
// 当团队id存在时才能发起请求
enabled: !!(group && group.id),
}
);
最佳实践:
- 将查询依赖的变量参数,写在查询的key中,本例中是 date和department
- key中的依赖参数,使用
['statData', { date, department }]而非['statData', date, department]。使用对象,不管对象中键值的顺序如何,都会被认为是相等的,react-query才能准确的复用他们 - 为了使 React Query 确定查询错误,查询函数的错误必须抛出,
throw new Error('Oh no')
2.列表useInfiniteQuery
代码如下(示例):
// 列表数据
const {
data, // 数据
fetchNextPage, // 请求下一页的方法
hasNextPage, // 是否还有下一页
isLoading, // loading
} = useInfiniteQuery(
// 参数1:query key
['statisticsByMonthList', { date, group }],
// 参数2:query function
async ({ pageNum = 1 }) => {
const { data: dataObj } = await fetchList({
year: date.getFullYear(),
month: date.getMonth(),
groupId: group ? group.id : '',
pageNum,
pageSize: 10,
});
// 数据处理并返回
// 下边的getNextPageParam的入参lastPage就是这个
return {
list: dataObj?.list ?? [],
pageNum, // 记录当前页码
total: dataObj?.total,
hasNextPage: dataObj?.hasNextPage, // 计算出是否有下一页
};
},
// 配置
{
// 团队id存在才能请求
enabled: !!(group && group.id),
// 获取fetchNextPage函数的入参,因为query function只有一个入参pageNum
// 所以这里返回下一页的页码即可
getNextPageParam: (lastPage) => {
if (lastPage?.hasNextPage) {
return lastPage.pageNum + 1; // 计算下一页页码
}
},
}
);
注意理解:
- query function的返回作为getNextPageParam的入参
- getNextPageParam的返回作为fetchNextPage的入参
- fetchNextPage实际上调用的也是query function,所以query function和getNextPageParam的返回,互为对方的入参
3. 并行查询
- 手动并行查询,只要连续使用任意数量的
useQuery和useInfiniteQuery即可 - 使用useQueries,入参是一个function,它返回一组useQuery的查询对象
{ queryKey, queryFn,queryConfig } - 当并行查询有依赖时,比如a获取用户,b通过用户id获取用户订单,使用config 中的
enable,即可告诉b,当a获取结果,得到id之后再运行
4. 状态指示器
- isFetching 表示该query正在后台重新请求
- 另外还有
const isFetching = useIsFetching();钩子,作为全局加载指示器
5. 禁用/暂停查询
query config 中的enable 为 false 时:
- 如果该query已经缓存了数据,则isSuccess
- 若该query没有缓存的数据,则isIdle
- 不会自动获取数据
- 手动 refetch可以触发query
6. 分页/滞后查询 keepPreviousData
平时的分页,默认展示第一页,翻第二页会请求一次,这时翻第一页又会请求一次,因为每个新页面都被视为一个全新的查询。
对于分页查询,让uerQuery的config,增加配置:keepPreviousData: true,,此时:
- 翻第二页,第一页的数据仍然可用
- 翻页时会显示新数据
- 可以使用isPreviousData来了解当前是什么数据
7. 查询数据占位符 placeholder-query-data
比如请求一个博客列表,当新用户的列表为空,写一个placeholderData,能够在列表为空时,展示一个示例的博客的mock数据
8. 修改 mutations
使用mutations去修改状态,注意,因为 mutation.mutate是异步方法, 在React16及之前的版本,由于React事件池的限制,使用时要用同步方法包裹,不能直接放在 onSubmit上
9. 查询失效 query-invalidation
之前说了,query获取的数据会被缓存,但缓存什么时候失效呢?我们可以通过:
// 使缓存中的每个查询都无效
queryClient.invalidateQueries();
// 无效每个查询键值以 `todos` 开头的查询
queryClient.invalidateQueries("todos");
手动的让缓存失效,失效后该query会被标记为过时,再次使用时该query会在后台重新获取数据
10. 修改导致的失效invalidation-from-mutations
例如:在增加todo的mutation的回调中让列表失效,这样,addTodo之后,就会重新获取列表数据
import { useMutation, useQueryClient } from "react-query";
const queryClient = useQueryClient();
// 当此修改成功时,将所有带有`todos`和`reminders`查询键值的查询都无效
const mutation = useMutation(addTodo, {
onSuccess: () => {
queryClient.invalidateQueries("todos");
queryClient.invalidateQueries("reminders");
},
});
11. 响应更新的数据 updates-from-mutation-responses
当我们更新一个数据对象时,新的数据通常会在更新成功后返回,此时我们可以通过queryClient.setQueryData方法,在前端直接更新对象,从而避免重新再请求一次
const queryClient = useQueryClient();
// 更新成功回调中,setQueryData,在todo query中找到id为5的
const mutation = useMutation(editTodo, {
onSuccess: (data) => {
queryClient.setQueryData(["todo", { id: 5 }], data);
},
});
mutation.mutate({
id: 5,
name: "Do the laundry",
});
// 下面的查询将使用成功的更新响应来进行更新
const { status, data, error } = useQuery(["todo", { id: 5 }], fetchTodoByID);
总结
常用功能就是这些,具体的细节还是需要在开发的过程中查文档
本文总结了React-Query的使用,包括其在处理服务端状态的优势,如自动管理loading、error,智能缓存与更新策略。文章详细介绍了useQuery、useInfiniteQuery、并行查询、状态指示器、查询禁用、分页处理、数据占位符、mutations及其引发的缓存失效等常见用例,旨在提供React应用中高效的数据管理解决方案。
3181





