写在前面
由于国内较少有比较系统的react-query教程,因此笔者结合官方文档以及官方课程的内容,希望写一个较为全面的教程。本文将以各种例子作为切入点,尽可能通俗易懂地讲解相关知识点。如果有错误,还请大家在评论区指出,笔者会尽快改正。
由于短篇的编写较为仓促,后期会总结一遍万字长文,重新根据短篇内容整理一篇完整教程。
目录
- 入门react-query 已于2022-06-04更新
- 深入查询键及查询函数 已于2022-06-08更新
- 并行请求及依赖请求 已于2022-06-19更新
- 查询结果缓存状态与调试工具 已于2022-06-23更新
- 处理错误 已于2022-06-26更新
- 使用QueryClient已于2022-07-27日更新
请各位读者回忆一下在第一章中,在index.ts
文件内初始化react-query
的代码:
import ReactDOM from "react-dom";
import App from "./App";
+ import { QueryClient, QueryClientProvider } from "react-query";
+ const queryClient = new QueryClient();
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(
+ <QueryClientProvider client={queryClient}>
<App />
+ </QueryClientProvider>
);
在添加的代码中,除了QueryClientProvider
外,最显眼的应该是QueryClient
。
在react-query中QueryClient
在后台默默地处理着请求及数据缓存的工作。
react-query有很多默认的配置,如果你想创建一个全局配置,可以在new QueryClient()
时传入。如果你希望请求的数据过期时间和缓存时间均是10s
且请求遇到错误后只重试1
次,你可以进行如下的配置:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 10 * 1000,
cacheTime: 10 * 1000,
retry: 1,
},
},
})
如果你有兴趣,可以在
react-query
官方文档中查找到更多配置
接下来将介绍一个默认查询函数的小技巧
默认查询函数
如果查询键可以很好地描述后端接口的路径地址,那么完全可以全局默认的查询函数来减少代码量(一般适合不复杂的项目,比如自建简单博客一类的)。
下面还是以github查询仓库issue列表的api举例:
https://api.github.com/repos/${org}/${repo}/issues
可以设置下面的查询键
const queryKey = ["repos", org, repo, "issues"]
由于查询函数中可以拿到queryKey
的内容,因此我们完全可以通过添加/
拼接请求接口,解放双手,写一遍查询函数即可。
const queryGithub = ({queryKey}) => {
const BASE_URL = `https://api.github.com/`
const apiPath = queryKey.join('/')
const requestUrl = `${BASE_URL}${apiPath}`
return fetch(requestUrl).then(res => res.json())
}
但是还有个问题,如果需要添加查询参数,此时上面的代码就不好用了,还需要对它进行扩展:
在下面的例子中,需要引入qs
库,全部代码如下:
import * as React from 'react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools';
import qs from 'qs';
import App from './App';
const queryGithub = ({ queryKey }) => {
const BASE_URL = `https://api.github.com/`;
const lastQueryKey = queryKey[queryKey.length - 1];
let queryParams = typeof lastQueryKey === 'object' ? qs.stringify(lastQueryKey) : '';
const apiPath = queryKey.join('/');
const requestUrl = `${BASE_URL}${apiPath}${queryParams}`;
return fetch(requestUrl, {
headers: {
Authorization: '',
},
}).then((res) => res.json());
};
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryFn: queryGithub,
},
},
});
root.render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools />
</QueryClientProvider>
</StrictMode>
);
如果不想用默认的查询函数,你也可以传入查询函数,这样就会覆盖默认的查询参数。
在项目中灵活使用这个技巧,来减少项目中的代码量,解放双手🙌🏻
使用queryClient请求数据
在项目的组件中,也可以调用queryClient
,使用useQueryClient
钩子,就可以获取queryClient
import { useQueryClient } from 'react-query'
// 此处省略一堆代码
const queryClient = useQueryClient();
除了使用useQuery
请求数据外,在特殊场景下(如:强制触发查询等)可以使用queryClient.fetchQuery
请求数据。
请注意,该方法是异步方法,因此如果使用
await
需要用try/catch
包裹一下。
把上一个例子中的代码稍加改造:
import * as React from 'react';
import { useQueryClient } from 'react-query';
import qs from 'qs';
import './style.css';
export default function App() {
const queryClient = useQueryClient();
const queryGithub = ({ queryKey }) => {
const BASE_URL = `https://api.github.com/`;
const lastQueryKey = queryKey[queryKey.length - 1];
let queryParams =
typeof lastQueryKey === 'object' ? qs.stringify(lastQueryKey) : '';
const apiPath = queryKey.join('/');
const requestUrl = `${BASE_URL}${apiPath}${queryParams}`;
return fetch(requestUrl, {
headers: {
Authorization: '',
},
}).then((res) => res.json());
};
const handleFetchIssues = async () => {
try {
const data = await queryClient.fetchQuery(
['repos', 'facebook', 'react', 'issues'],
queryGithub
);
console.log(data);
} catch (e) {
console.log(e);
}
};
return (
<div>
<button onClick={handleFetchIssues}>获取数据</button>
</div>
);
}
在上面使用queryClient.fetchQuery
请求时
- 如果在缓存中有对应的数据(通过查询键匹配)且未过期,你可以立即得到结果,无需请求。
- 如果没有缓存或缓存已经过期,那么react-query将会重新请求并且缓存数据。
如果是一般组件内的请求,还是建议使用useQuery
,而不是queryClient.fetchQuery
。