目录
- 入门react-query 已于2022-06-04更新
- 深入查询键及查询函数 已于2022-06-08更新
- 并行请求及依赖请求 已于2022-06-19更新
- 查询结果缓存状态与调试工具 已于2022-06-23更新
版本说明
本教程基于react-query@4
版本编写,此版本目前(2022-06-08)为alpha版本。
在线演示基于stackblitz
平台
查询键常见问题
查询键的值不能重复,需要保持唯一
想象一下这样的场景,在localStorage中设置了下面的数据,目的是以userID为键,把当前的用户数据保存到缓存内
const userId = 1;
localStorage.setItem(userId, {username: 'fed-orange'})
接着再把某商品的内容以ProductId为键,把当商品的数据保存到缓存内
const productId = 1;
localStorage.setItem(productId, {name: 'orange'})
此时,会发现你保存到localStorage仅仅只保存了你后面缓存的值。
同理,在react-query中,如果这么做同样会遇到这样的问题 请求用户数据
const usersQuery = useQuery(
[userId],
fetchUsers
);
请求商品数据
const productsQuery = useQuery(
[productId],
fetchProducts
);
如果userId
与productId
相同时,后面请求的数据会覆盖前面请求的数据!解决这个问题的办法是:可以在数组的第一个元素中,放一个字符串来标识当前的数据类型(可以参照笔者之前提到的设计查询键的小建议中的内容),就可以解决这个问题。
const usersQuery = useQuery(
['users', userId],
fetchUsers
);
const productsQuery = useQuery(
['products', productId],
fetchProducts
);
这样做不仅能更好的写查询键,在你调试数据的过程里,在DevTools中你能很方便的区分出来不同的数据(不要自己给自己挖坑,在DevTools是以查询键为维度列出所有缓存的数据)!
查询函数
查询函数使用小妙招
相信在之前的介绍里面,可能把你的思维禁锢在查询函数只能使用fetch(或者axios之类的库)来调用http接口。 但是实际上,所有的Promise函数,都可以作为查询函数。举个例子,浏览器中异步的API接口——获取当前定位的API,可以封装为一个查询函数。
const getLocation = async () =>
new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
接着在useQuery
中使用上面的查询函数
例子1👇🏻
import * as React from 'react';
import { useQuery } from 'react-query';
export default function App() {
const getLocation = async () =>
new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(resolve, reject);
});
const locationQuery = useQuery(['location'], getLocation);
return (
<div>
<h1>获取地理位置</h1>
{locationQuery.isLoading ? (
'地理位置获取中'
) : locationQuery.isError ? (
locationQuery?.error?.message
) : (
<p>
你的地理位置是:{locationQuery.data.coords.latitude},
{locationQuery.data.coords.longitude}
</p>
)}
</div>
);
}
自定义hook封装react-query的查询请求
前面举的例子中,很多的请求都非常简单,并且仅仅在一个地方使用,因此我们无需对代码进行封装。但是假如请求本身涉及到非常复杂的处理,并且可能在多个组件重复使用的时候,我们又回到了最初的原点,发现很多重复代码需要复制粘贴!但是一旦你有这个念头的时候,就是万恶的开始,一旦你这么做了,后期的维护成本将大大增加,修改一个地方,你不得不把类似的代码都更改一遍,费时费力!
下面我们把之前的请求仓库issue的例子抽离成自定义hook,这个是Github中针对该api的相关说明文档,有兴趣的同学可以自行查阅:issues api文档,如果想扩展相关功能欢迎在stackblitz里fork后自行尝试。
我们再来看一下之前的查询函数
const fetchData = ({ queryKey }) => {
const [, owner, repo] = queryKey;
return fetch(`https://api.github.com/repos/${owner}/${repo}/issues`, {
headers: {
Authorization: '',
},
}).then(async (response) => {
// 如果请求返回status不为200 则抛出后端错误
if (response.status !== 200) {
const { message } = await response.json();
throw new Error(message);
}
return response.json();
});
};
接下来我们定义一个名为useGithubIssuesQuery
的钩子
import { useQuery } from 'react-query';
const useGithubIssuesQuery = ({ owner, repo }) => {
const fetchData = ({ queryKey }) => {
const [, owner, repo] = queryKey;
return fetch(`https://api.github.com/repos/${owner}/${repo}/issues`, {
headers: {
Authorization: '',
},
}).then(async (response) => {
// 如果请求返回status不为200 则抛出后端错误
if (response.status !== 200) {
const { message } = await response.json();
throw new Error(message);
}
return response.json();
});
};
return useQuery(['issues', owner, repo], fetchData);
};
export default useGithubIssuesQuery;
此时在react组件获取数据时,要做的仅仅是使用useGithubIssuesQuery
钩子,传入owner及repo参数即可,其它的一切都不需要关心,如果在别的组件中需要请求仓库issue相关的接口,只要使用useGithubIssuesQuery
钩子即可。并且在后期添加功能时,只需要找到对应的自定义钩子并做相关改动即可,大大提高了开发效率。
在多个组件中引用相同key值的数据,react-query不会进行多次请求,可以放心使用!
总结
查询键
- 请保证查询键的唯一性。如果有重复的查询键,将会导致数据覆盖问题
- 查询键的设计可以遵循从通用到特定的规则,第一个元素可以是字符串来描述数据类型
- 查询键的元素可以是字符串、数字、嵌套数组、对象
- 在查询键变化时(包括对象内的变化,react-query将会进行深度比较),react-query将会重新调用查询函数
查询函数
- 查询函数可以是任何Promise函数,包括但不局限于fetch、axios、graphql请求、异步浏览器api等
其它
建议针对需要复杂处理且在多处使用的请求,封装自定义钩子来提高开发效率。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。