并行申请及依赖申请
写在后面
因为国内较少有比拟零碎的react-query教程,因而笔者联合官网文档以及官网课程的内容,心愿写一个较为全面的教程。本文将以各种例子作为切入点,尽可能通俗易懂地解说相干知识点。如果有谬误,还请大家在评论区指出,笔者会尽快改过。
目录
- 入门react-query 已于2022-06-04更新
- 深刻查询键及查问函数 已于2022-06-08更新
- 并行申请及依赖申请 已于2022-06-19更新
并行申请
什么是并行申请?
在日常开发中,前端申请后端接口时,通常会申请多个接口。比方上面的react-query代码:
const usersQuery = useQuery(['users'], fetchUsers) const teamsQuery = useQuery(['teams'], fetchTeams) const projectsQuery = useQuery(['projects'], fetchProjects)
下面的示例中,申请了用户列表,团队列表,我的项目列表,此时的接口申请就是并行申请,每个申请互不烦扰(你同样能够在数据库查问中,看到相干的概念,Google一下Parallel Queries
)。谁先申请到数据,就显示哪一项数据。
假如当初有一个需要:别离展现github用户的仓库和代码片段列表 此时咱们须要申请两个接口
获取用户仓库列表:
https://api.github.com/users/{username}/repos
获取用户代码片段列表:
https://api.github.com/users/{username}/gists
对这两个接口别离同时进行申请,哪个接口先返回了数据,就显示哪个接口的数据:
点我查看在线演示
import { useQuery } from "react-query";const ReposAndGists = ({ username }) => { const reposQuery = useQuery( ["repos", username], () => { return fetch( `https://api.github.com/users/${username}/repos` ) .then((res) => res.json()) } ); const gistQuery = useQuery( ["gists", username], () => { return fetch( `https://api.github.com/users/${username}/gists` ) .then((res) => res.json()) } ); return ( <div> <h2>仓库列表</h2> {reposQuery.isLoading && <p>加载仓库列表中...</p>} {reposQuery.isError && ( <p>加载仓库列表产生谬误: {reposQuery.error.message}</p> )} {reposQuery.data && ( <ul> {reposQuery.data.map((repo) => ( <li key={repo.id}>{repo.name}</li> ))} </ul> )} <h2>代码片段列表</h2> {gistQuery.isLoading && <p>加载代码片段列表中...</p>} {gistQuery.isError && ( <p>加载代码片段列表谬误: {gistQuery.error.message}</p> )} {gistQuery.data && ( <ul> {gistQuery.data.map((gist) => ( <li key={gist.id}>{gist.description}</li> ))} </ul> )} </div> );};
另外一种状况是,心愿对两个接口进行合并申请,等到两个接口都返回数据时,再向用户展现内容
在查问函数中应用Promise.all合并某两个申请
假如当初有一个需要:同时展现github用户的仓库和代码片段列表。此时后端并没有新的接口,你须要复用原有的老接口:
获取用户仓库列表:
https://api.github.com/users/{username}/repos
获取用户代码片段列表:
https://api.github.com/users/{username}/gists
需要的要求是等到所有数据返回后,才给用户显示内容。此时就须要应用Promise.all
来合并申请。(ps:是否想起了被手写Pormise.all摆布的恐怖)
上面是示例代码,同时应用github的username作为参数,申请该用户的仓库列表和代码片段列表
点我查看在线演示
import * as React from 'react';import { useQuery } from 'react-query';const getReposAndGists = (username) => { return Promise.all([ fetch(`https://api.github.com/users/${username}/repos`).then((res) => res.json() ), fetch(`https://api.github.com/users/${username}/gists`).then((res) => res.json() ), ]);};const ReposAndGists = ({ username }) => { const reposAndGistsQuery = useQuery(['reposAndGists', username], () => getReposAndGists(username) ); if (reposAndGistsQuery.isLoading) { return <p>加载数据中...</p>; } if (reposAndGistsQuery.isError) { return <p>数据加载谬误: {reposAndGistsQuery.error.message}</p>; } if (!reposAndGistsQuery.data) { return null; } const [repos, gists] = reposAndGistsQuery.data; return ( <div> <h2>仓库列表</h2> <ul> {repos.map((repo) => ( <li key={repo.id}>{repo.name}</li> ))} </ul> <hr /> <h2>代码片段列表</h2> <ul> {gists.map((gist) => ( <li key={gist.id}>{gist.description || '暂无形容'}</li> ))} </ul> </div> );};export default ReposAndGists;
应用useQueries进行申请
如果须要动静生成申请,你会发现,在现有的hooks规定下应用useQuery
无奈实现这个需要。
因而react-query提供了useQueries
来解决这个问题,上面的代码中,咱们能够随便批改数组内容,此时react-query就会生成不同的申请:
上面的例子中,首先批量申请获取了数组内以下用户的所有仓库['facebook', 'vuejs', 'nestjs', 'mongdb']
,在点击按钮后,从新批量获取了以下用户的仓库['microsoft', 'tesla']
,做到了十分不便的动静申请。
点我查看在线演示
import * as React from 'react';import { useQueries } from 'react-query';export default function App() { const [users, setUsers] = React.useState([ 'facebook', 'vuejs', 'nestjs', 'mongodb', ]); const getRepos = (username) => fetch(`https://api.github.com/users/${username}/repos`).then((res) => res.json() ); const userQueries = useQueries({ queries: users.map((user) => { return { queryKey: ['user', user], queryFn: () => getRepos(user), }; }), }); return ( <div> <h1>查看github用户的仓库</h1> <button onClick={() => setUsers(['microsoft', 'tesla'])}> 更改获取用户 </button> {userQueries.map((query) => query.isLoading ? ( <div>加载中....</div> ) : ( <ol> {query.data.map((item) => ( <li>{item.full_name}</li> ))} </ol> ) )} </div> );}
依赖申请
什么是依赖申请?
假如当初有一个需要:给用户展现最新的一条的内容详情。然而现实情况是后端只提供了按工夫倒序的列表接口和详情接口,并没有工夫给你出新的接口。
此时能够想到的方法是:获取列表接口的第一条数据id之后再申请详情页接口,将获取的详情内容展现给用户。
这种状况就是依赖申请,申请B接口的某个参数依赖A接口申请返回的内容。
上面还是应用github的接口,给大家演示一个需要:获取react仓库标签列表中的第二个标签,申请这个标签的issue列表。
点我查看在线演示
import * as React from 'react';import { useQuery } from 'react-query';const IssueLabelFilter = ({ owner, repo }) => { const labelsQuery = useQuery(['repos', owner, repo, 'labels'], () => fetch(`https://api.github.com/repos/${owner}/${repo}/labels`).then((res) => res.json() ) ); const labels = labelsQuery.data; const issuesQuery = useQuery( ['repos', owner, repo, 'issues'], () => fetch( `https://api.github.com/repos/${owner}/${repo}/issues?labels=${labels[1].name}` ).then((res) => res.json()), { /** * 该配置项在value为true时,会触发接口申请。 * 因而当第一个接口申请返回后,此时该配置项表达式为true * 会触发申请github中的issue列表 * ① */ enabled: !!labels, } ); return ( <div> <h2>标签</h2> {labelsQuery.isLoading ? ( <p>加载标签中...</p> ) : ( <ul> {labelsQuery.data.map((label) => ( <li key={label.id}>{label.name}</li> ))} </ul> )} <hr /> <h2> Issues {Array.isArray(issuesQuery.data) ? `(${labels[1].name})` : ''} </h2> {issuesQuery.isLoading ? ( <p>加载issues中...</p> ) : ( <ul> {issuesQuery.data.map((issue) => ( <li key={issue.id}>{issue.title}</li> ))} </ul> )} </div> );};export default function App() { return ( <div> <IssueLabelFilter owner={'facebook'} repo={'react'} /> </div> );}
在react-query中,当该组件被加载时,组件内的useQuery就会开始申请。
此时显著不合乎需要,需要的要求是在加载完标签列表后,获取到第二个标签,再开始申请issues列表。
因而就须要应用到useQuery
的enabled
参数,当参数值为false时,将会禁止申请接口。
当初回到下面的例子中,当labelsQuery
申请还没有后果时,labels
变量值为undefined
,此时在①行代码中的值为false
,当labelsQuery
申请完结时,labels
变量值为数组,此时在①行代码中的值为true
,issuesQuery
开始申请数据。完全符合需要的要求。
改良依赖查问接口始终为loading的问题
下面的例子中,issuesQuery
在加载后,因为处于禁用状态(配置项enabled: false
),此时isLoading
将会始终处于true
的状态,直到issuesQuery
申请实现数据后变为false
。
这种提醒会十分奇怪,明明有一段时间里issuesQuery
没有真正的申请数据,为啥要始终显示加载标签中...
的内容?
解决办法是:须要一个字段来辨别查问函数以后并没有申请数据,处于摸鱼状态。
在useQuery
中当fetchStatus
字段在为idle
时,示意以后查问函数不在运行,处于摸鱼状态^ ^
fetchStatus
一共有三个状态,fetching
状态示意以后查问函数正在运行,idle
状态示意过后查问函数不在运行。paused
状态示意查问函数尝试运行,然而无奈进行申请,最可能的起因是因为以后没有联网,处于离线状态。
点我查看在线演示
import * as React from 'react';import { useQuery } from 'react-query';import './style.css';const IssueLabelFilter = ({ owner, repo }) => { const labelsQuery = useQuery(['repos', owner, repo, 'labels'], () => fetch(`https://api.github.com/repos/${owner}/${repo}/labels`).then((res) => res.json() ) ); const labels = labelsQuery.data; const issuesQuery = useQuery( ['repos', owner, repo, 'issues'], () => fetch( `https://api.github.com/repos/${owner}/${repo}/issues?labels=${labels[1].name}` ).then((res) => res.json()), { enabled: !!labels, // ❗️❗️❗️该配置项在value为true时,会触发接口申请。因而当第一个接口申请返回后,此时该配置项表达式为true,会触发申请github中的issue列表 } ); return ( <div> <h2>标签</h2> {labelsQuery.isLoading ? ( <p>加载标签中...</p> ) : ( <ul> {labelsQuery.data.map((label) => ( <li key={label.id}>{label.name}</li> ))} </ul> )} <hr /> // 上面的代码判断了在查问函数处于摸鱼状态时,不显示任何内容 ② {issuesQuery.isLoading && issuesQuery.fetchStatus === 'idle' ? null : ( <div> <h2> Issues {Array.isArray(issuesQuery.data) ? `(${labels[1].name})` : ''} </h2> // 当查问函数处于干活状态时,显示加载issues中 ③ {issuesQuery.isLoading ? ( <p>加载issues中...</p> ) : ( <ul> {issuesQuery.data.map((issue) => ( <li key={issue.id}>{issue.title}</li> ))} </ul> )} </div> )} </div> );};export default function App() { return ( <div> <IssueLabelFilter owner={'facebook'} repo={'react'} /> </div> );}
在代码②中,当查问函数处于摸鱼状态时,不显示任何内容。在代码③中的isLoading
是查问函数正在在干活的时候,此时咱们把加载issues中...
状态显示进去。
总结
并行申请
- 通过在查问函数中,应用
Promise.all
来进行接口的合并申请 - 通过useQueries来进行动静的并行申请
- 通过在查问函数中,应用
依赖申请
isLoading
为true
示意第一次申请未返回后果前这段时间的状态,如果对接口进行了禁用,能够通过fetchStatus
为idle
来获取接口禁用申请这段时间的状态。