乐趣区

关于react.js:reactquery手把手教程③并行请求及依赖请求

并行申请及依赖申请

写在后面

因为国内较少有比拟零碎的 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 列表。

因而就须要应用到 useQueryenabled参数,当参数值为 false 时,将会禁止申请接口。

当初回到下面的例子中,当 labelsQuery 申请还没有后果时,labels变量值为 undefined, 此时在①行代码中的值为false,当labelsQuery 申请完结时,labels变量值为数组,此时在①行代码中的值为 trueissuesQuery 开始申请数据。完全符合需要的要求。

改良依赖查问接口始终为 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 来进行动静的并行申请
  • 依赖申请

    • isLoadingtrue 示意第一次申请未返回后果前这段时间的状态,如果对接口进行了禁用,能够通过 fetchStatusidle来获取接口禁用申请这段时间的状态。
退出移动版