请求数据管理
React中,我们可以直接使用XHR、fetch、axios等进行AJAX请求。但在实际开发中,请求接口时经常有很多类似的功能需要重复编写,比如获取请求状态、分页加载、缓存等。react-query是一个目前比较流行的请求管理库,它封装了这些功能并和React Hooks能够良好的集成到一起,因此比较推荐使用。react-query的作者是美国程序员Tanner Linsley。
官方文档:https://tanstack.com/query/latest
Github地址:https://github.com/tanstack/query
安装React Query
react-query的最新版本是v4版本,我们执行以下命令安装:
npm install @tanstack/react-query --save
React Query基本使用
React Query提供了很多和异步请求有关的常用功能最佳实践,API也比繁杂。我们这里先不管那么多,先通过一个例子观察React Query如何配置使用,后面再讲解具体的参数和用法。下面例子中,我们使用React Query实现了一个最简单的Fetch请求管理。
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import StudentManage from "./StudentManage";
const queryClient = new QueryClient();
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<StudentManage />
</QueryClientProvider>
</React.StrictMode>
);
StudentManage.jsx
import { useQuery } from "@tanstack/react-query";
const StudentManage = () => {
const getStudentList = async () => {
const rsp = await fetch("/api/getStudentList", {
method: "GET",
});
return await rsp.json();
};
const { isLoading, data } = useQuery({
queryKey: ["studentList"],
queryFn: getStudentList,
enabled: true,
});
return <div>{isLoading ? "Loading..." : JSON.stringify(data)}</div>;
};
export default StudentManage;
index.js中我们创建了QueryClient对象,并将其赋予<QueryClientProvider>组件,这些组件提供了处理请求和缓存的功能,我们将其在根组件上配置好,子组件便能使用Hooks函数操作相关的功能。
StudentManage.jsx中,getStudentList()是我们自定义的异步函数,它使用fetch()请求数据。我们使用了useQuery对这个请求函数再次进行了封装,useQuery提供了丰富的功能对请求进行管理,我们这里使用的isLoading属性就能够方便的读取请求状态,配置为enabled: true它便可以在页面加载后自动执行(enabled默认值为true,因此实际开发中如果需要请求自动执行不需要显示的写出,这里仅作为示例)。
JSX中,页面刷新后请求会自动执行,我们在请求过程中显示“Loading...”字样,请求完成后显示接口返回的数据。
useQuery配置
useQuery的参数是一个对象,其中指定了请求的配置信息。
useQuery(queryKey, queryFn, otherOptions)
下面我们详细介绍useQuery的使用方法。
queryKey
queryKey是对应一个请求的唯一标识,React Query通过这个唯一标识判断哪个请求需要更新。queryKey是一个数组,它可以接收多个参数,一般我们会在第1个参数中传递请求名字(字符串),后面跟着请求的参数。
const { isLoading, data } = useQuery({
queryKey: ["studentList", classId],
queryFn: async ({ queryKey }) => {
const rsp = await fetch("/api/getStudentList?classId=" + queryKey[1], {
method: "GET",
});
return await rsp.json();
},
enabled: true,
});
上面例子中,我们的queryKey第2个参数值就是请求的GET参数,它也会传递到queryFn中。当queryKey中的请求参数发生变化时,请求会被重新触发(这类似于useEffect的设计)。
queryFn
queryFn属性需要设置为一个异步函数,其内部逻辑就是调用接口,我们可以自己选用axios、Fetch等方式请求接口,异步函数的返回值会被放入useQuery返回值的data属性中。
实际使用中,还会涉及到向接口传递参数问题。前面介绍过请求参数我们可以将其放在queryKey中,useQuery会将QueryFunctionContext参数传递给queryFn,我们可以在queryFn中通过QueryFunctionContext解构获取queryKey。
queryFn: async ({ queryKey }) => {};
获取请求返回数据
前面例子中我们已经演示如何获取请求返回的数据了,我们直接从useQuery的返回值中解构data属性即可。
const { isLoading, data } = useQuery({
queryKey: ["studentList"],
queryFn: getStudentList,
});
useQuery内部使用了useState来追踪组件的状态,并在数据改变时触发组件函数的重新执行。
获取请求状态
React Query的请求状态分为status和fetchStatus两部分,它们都可以通过useQuery的返回值解构获得。
status取值:
loading:加载中error:失败success:成功
fetchStatus取值:
paused:请求暂停idle:空闲fetching:正在请求
这些状态值都有对应的is属性供我们直接判断,比如isLoading、isError等。之前的例子代码中,我们就通过isLoading对当前状态是否为请求中进行了判断。在请求完成之前,data的值都是undefined。
注意:状态判断这里有一点比较坑,对于页面加载后自动开始的请求可以用if (isLoading) {},对于手动触发的请求我们则需要用if (isLoading && isFetching) {},因为非自动的请求isLoading在请求触发前一直都是true。
实现依赖请求
如果存在多个useQuery函数,它们会并发执行。但实际开发中,我们可能遇到需要依赖请求的情况,比如先调用funcA获取数据,再调用funcB获取数据,没有前一步的返回数据后一步是无法执行的。React Query中我们可以通过设置enabled属性来实现依赖请求。
const { data: dataA } = useQuery({
queryKey: ["funcA"],
queryFn: funcA,
});
const { data: dataB } = useQuery({
queryKey: ["funcB"],
queryFn: funcB,
enabled: !!dataA,
});
上面例子中,在获取到dataA数据之前,其值为undefined,因此funcB的enabled属性为false;funcA执行完成dataA在useQuery内部被赋值,同时触发组件函数的重新执行,此时dataA有数据因此funcB的enabled属性变为true,funcB执行。