乐趣区

关于react.js:reactquery手把手教程①入门reactquery

入门 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 中有许多仓库,这些仓库通常以用户作为第一级标识,仓库名是第二级标识,如下图所示

因而第二个和第三个参数顺次是 ownerrepo

下面的例子中,咱们没有应用 ['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 的缓存信息,帮忙你了解:

退出移动版