入门react-query
写在后面
因为国内较少有比拟零碎的react-query教程,因而笔者联合官网文档以及官网课程的内容,心愿写一个较为全面的教程。本文将以各种例子作为切入点,尽可能通俗易懂地解说相干知识点。如果有谬误,还请大家在评论区指出,笔者会尽快改过。
目录
- [入门react-query]() 已于2022-06-05更新
版本阐明
本教程基于react-query@4
版本编写,此版本目前(2022-06-05)为alpha版本。
在线演示基于stackblitz
平台
从在react中后端申请数据开始说起
在日常的开发中,免不了申请后端接口。在申请接口时,常常会波及到以下解决
- 加载状态
- 后端返回数据存储
- 如果接口有报错信息,展现报错信息
- 刷新数据
等等
上面来看一个满足了上述解决的例子
点我查看例子①在线演示
PS:以下代码将会从github申请一句富裕禅意(逼格高)的话,并显示在页面上
例子①
import * as React from 'react';export default function App() { // 存储 后端返回数据 const [zen, setZen] = React.useState(''); // 存储 加载状态 const [isLoading, setIsLoading] = React.useState(false); // 存储 是否申请胜利 const [isError, setIsError] = React.useState(false); // 存储 后端返回的谬误数据 const [errorMessage, setErrorMessage] = React.useState(''); const fetchData = () => { // 开始获取数据,将isLoading置为true setIsLoading(true); fetch('https://api.github.com/zen') .then(async (response) => { // 如果申请返回status不为200 则抛出后端谬误 if (response.status !== 200) { const { message } = await response.json(); throw new Error(message); } return response.text(); }) .then((text: string) => { // 申请实现将isLoading置为false setIsLoading(false); // 接口申请胜利,将isError置为false setIsError(false); // 存储后端返回的数据 setZen(text); }) .catch((error) => { // 申请实现将isLoading置为false setIsLoading(false); // 接口申请谬误,将isError置为true setIsError(true); // 存储后端返回的谬误数据 setErrorMessage(error.message); }); }; React.useEffect(() => { // 初始化申请数据 fetchData(); }, []); return ( <div> <h1>Zen from Github</h1> <p>{isLoading ? '加载中...' : isError ? errorMessage : zen}</p> {!isLoading && ( <button onClick={fetchData}>{isError ? '重试' : '刷新'}</button> )} </div> );}
在下面的例子中
- 应用
isLoading
来存储加载状态
- 应用
isError
来存储接口是否有谬误
- 应用
errorMessage
来存储后端返回的报错信息
- 应用
zen
来存储后端返回数据存储
- 从新调用
fetchData
办法来刷新数据
该例子仅仅是申请一个接口,如果是一个实在的我的项目,铁定不止这一个申请,因而咱们将要写大量反复代码,来满足业务需要(心田os:其实是代码写的太多影响效率,不能早上班(¬_¬),而且保护起来老本高)_
此时引入react-query能够缩小与申请接口相干的代码,下面的例子应用react-query重写如下:
点我查看例子②在线演示
例子②
import * as React from 'react';import { useQuery } from 'react-query';const fetchData = () => { return fetch('https://api.github.com/zen').then(async (response) => { // 如果申请返回status不为200 则抛出后端谬误 if (response.status !== 200) { const { message } = await response.json(); throw new Error(message); } return response.text(); });};export default function App() { const zenQuery = useQuery(['zen'], fetchData); // ① return ( <div> <h1>Zen from Github</h1> <p> {zenQuery.isLoading || zenQuery.isFetching ? '加载中...' : zenQuery.isError ? zenQuery.error?.message : data} </p> {!zenQuery.isLoading && !zenQuery.isFetching && ( <button onClick={() => { zenQuery.refetch(); }} > {zenQuery.isError ? '重试' : '刷新'} </button> )} </div> );}
比照一下,在引入了react-query之后,肉眼可见代码量在升高!!
不须要写useState来治理因为申请接口带来的额定状态(如果应用react-redux、mobx等状态治理库,同样会遇到相似的问题),同样也不须要在useEffect(() => {}, [])
中初始化调用接口,react-query会帮咱们解决。
你可能对于下面例子中,在代码①引入useQuery
钩子时,传入的参数不是很理解,接下来咱们将会介绍这些传入的参数。
查询键(Query Keys)及查问函数(Query Functions)
什么是查询键(Query Keys)及查问函数(Query Functions)?
在大家日常开发的过程中,申请后端数据时:
- 会先写一个函数来申请后端接口的数据(如下面例子①中的
fetchData
函数) - 接着指定一个变量(如下面例子①中的
zen
)来存储相干后端返回的数据,每个接口的变量针对不同的接口会起不同的名字,来标识不同的数据。
那么在react-query中如何辨别不同的接口获取的不同数据呢?
回到例子②中,咱们应用useQuery
钩子来获取后端数据,代码如下:
const zenQuery = useQuery(['zen'], fetchData);
- 其中
['zen']
就是react-query的查询键,react-query通过不同的查询键来标识(映射)不同接口(或是同一接口不同参数申请)返回的数据。在react-query@4
中,查询键必须是数组。 - 而
fetchData
就是咱们申请后端接口的函数,也就是查问函数。
PS:查询键内的元素能够是嵌套数组、对象、字符串、数字
例如:
['zen', { form: 'confucius' }]
或['zen', ['confucius', 'Lao Tzu']]
为了不便记忆,打个比方,你能够将查询键看做是你存储localStorage
时的key,而value则是通过查问函数查问到数据后,将各种咱们须要的状态数据存储进入value
PS:当然理论的处理过程及存储的信息会很简单,不过思路基本上统一。
写查询键的一些小倡议
解释完查询键和查问函数后,笔者心愿大家思考一个问题,这个接口比较简单,因而咱们能够应用zen
来作为查询键,如果我有一个简单的接口,此时应该如何更好的设计查询键呢?
还是以github的接口为例,如果你想获取到github中某个仓库的issue列表,你能够这样调用接口
https://api.github.com/repos/{owner}/{repo}/issues
具体一点,如果心愿获取react仓库issue列表能够调用上面的接口,你能够在浏览器中关上尝试一下:
https://api.github.com/repos/facebook/react/issues
此时,你能够通过申请接口,拿到react仓库内的issue列表。
以这个获取仓库issue列表接口为例,能够这样写查询键
例子③
['issues', owner, repo]
在这个查询键中咱们遵循了一个准则:从通用到非凡
首先咱们获取的数据类型是issue,咱们须要在数组的结尾放一个字符串来标识数据类型,因而第一个参数咱们设定为issues
。在github中有许多仓库,这些仓库通常以用户作为第一级标识,仓库名是第二级标识,如下图所示
因而第二个和第三个参数顺次是owner
和repo
。
下面的例子中,咱们没有应用['issues', 'facebook', 'react']
而是应用['issues', owner, repo]
的起因是为了介绍在react-query中,应用变量作为查询键的元素时,当变量的值变动后,react-query将会从新调用fetchData
办法,获取新的数据,并缓存到对应变量值为key的缓存中。
即产生上面的变动时,react-query将会从新调用fetchData
办法,并将从后端获取到的数据,缓存在查询键为['issues', 'vuejs', 'vue']
对应的值中,同理咱们在初始化调用接口时,获取的数据时缓存在查询键为['issues', 'facebook', 'react']
的对应值中:
['issues', 'facebook', 'react'] -> ['issues', 'vuejs', 'vue'] // 从查问react仓库的issue,变更为查问vue仓库的issue
上面的例子将会获取react仓库中最新一条issue,你能够查看例子④的在线演示
将示例中输入框内的:facebook更换为vuejs,将react更换为vue,点击【查看最新issue信息】按钮,就能够看到vue仓库最新的issue信息(针对相干的数据缓存,你能够想一下下面咱们说过的例子)
点我查看例子④在线演示
例子④
import * as React from 'react';import { useQuery } from 'react-query';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(); });};export default function App() { const [inputOwner, setInputOwner] = React.useState('facebook'); const [inputRepo, setInputRepo] = React.useState('react'); const [queryKey, setQueryKey] = React.useState([inputOwner, inputRepo]); const issueQuery = useQuery(['issues', ...queryKey], fetchData); return ( <div> <span>仓库:</span> <input name={'owner'} value={inputOwner} onChange={(e) => setInputOwner(e.target.value)} /> / <input name={'repo'} value={inputRepo} onChange={(e) => setInputRepo(e.target.value)} /> <button onClick={() => { setQueryKey([inputOwner, inputRepo]); }} > 查看最新issue信息 </button> <div> <h1> 仓库{queryKey[0]}/{queryKey[1]}最新一条issue信息 </h1> <p> {issueQuery.isLoading ? '加载中...' : issueQuery.isError ? issueQuery.message : JSON.stringify(issueQuery.data[0])} </p> </div> </div> );}
在这个例子中,当查询键变量的值变动后,react-query将会主动申请变动后对应的数据,并且在查问函数传入的参数中,咱们也能够拿到调用查问函数时查询键的值。
你能够在DevTool中,查看react-query的缓存信息,帮忙你了解: