这是使用React Query与TypeScript的系列文章中的另一篇文章。
以前的文章:
这篇文章涵盖了如何用React Query取消获取请求。我们的实现将允许React Query为我们取消一个请求,如果它的组件被卸载时正在飞行中。我们还将使用户能够点击一个按钮来取消请求。

我们的起点
我们将从类似于我们在入门帖中完成的代码开始。
这就是获取函数的样子:
type Character = {
name: string;
};
type Params = {
queryKey: [string, { id: number }];
};
async function getCharacter(params: Params) {
const [, { id }] = params.queryKey;
const response = await fetch(`https://swapi.dev/api/people/${id}/`);
if (!response.ok) {
throw new Error("Problem fetching data");
}
const character = await response.json();
assertIsCharacter(character);
return character;
}
function assertIsCharacter(character: any): asserts character is Character {
if (!("name" in character)) {
throw new Error("Not character");
}
}
这是包含查询的组件:
function CharacterDetails() {
const { status, error, data } = useQuery<Character, Error>(
["character", { id: 1 }],
getCharacter
);
if (status === "loading") {
return <button>Cancel</button>;
}
if (status === "error") {
return <div>{error!.message}</div>;
}
return data ? <h3>{data.name}</h3> : null;
}
在获取数据时,有一个 "取消"按钮被呈现出来。当这个按钮被点击时,我们想取消查询。
提供一个取消请求的方法
上一篇文章介绍了如何用以下方法取消fetch 请求 AbortController.这包含一个可以传递给fetch 的signal 属性和一个可以用来取消请求的abort 方法。
让我们在获取函数中的AbortController 及其信号中使用fetch 请求:
interface PromiseWithCancel<T> extends Promise<T> {
cancel: () => void;
}
function getCharacter(params: Params) {
const [, { id }] = params.queryKey;
const controller = new AbortController();
const signal = controller.signal;
const promise = new Promise(async (resolve, reject) => {
try {
const response = await fetch(`https://swapi.dev/api/people/${id}/`, {
method: "get",
signal,
});
if (!response.ok) {
reject(new Error("Problem fetching data"));
}
const data = await response.json();
assertIsCharacter(data);
resolve(data);
} catch (ex: unknown) {
if (isAbortError(ex)) {
reject(new Error("Request cancelled"));
}
}
});
(promise as PromiseWithCancel<Character>).cancel = () => {
controller.abort();
};
return promise as PromiseWithCancel<Character>;
}
function isAbortError(error: any): error is DOMException {
if (error && error.name === "AbortError") {
return true;
}
return false;
}
我们将现有的代码包装在一个新的Promise 。Promise ,用请求中的数据来解决。如果请求被取消或不成功,该承诺将被拒绝,并出现适当的错误。
我们为返回的Promise 添加了一个cancel 方法,该方法调用AbortController.abort 。我们已经添加了PromiseWithCancel 接口,并在返回的承诺上使用这个接口,否则会发生类型错误。
取消查询
有一个 cancelQueries函数,可以在React Query客户端上调用来取消请求。
首先,我们需要获得一个对查询客户端的引用。我们可以使用useQueryClient 钩子来做到这一点:
import { useQuery, useQueryClient } from "react-query";...
function CharacterDetails() {
const queryClient = useQueryClient(); ...
}
然后可以在按钮点击事件上使用cancelQueries 函数:
function CharacterDetails() {
...
if (status === "loading") {
return (
<button onClick={() => queryClient.cancelQueries("character")}> Cancel
</button>
);
}
...
}
查询的取消键被传入cancelQueries ,在我们的例子中是"character" 。
如果我们在慢速连接上试一下,我们会看到当取消按钮被点击时,查询被取消了。

不错😊
当其组件被umounted时,取消飞行中的请求
默认情况下,React Query不会在其组件被挂起时取消飞行中的请求。然而,如果获取函数返回的承诺包含一个cancel 方法,它将调用这个方法,在其组件被卸载时取消一个飞行中的查询。
让我们试一试,在1秒后将组件从渲染树中移除:
export default function App() {
const [renderComponent, setRenderComponent] = React.useState(true);
React.useEffect(() => {
setTimeout(() => {
setRenderComponent(false);
}, 1000);
}, []);
return <div className="App">{renderComponent && <CharacterDetails />}</div>;
}
在慢速连接的情况下,1秒后获取请求仍在飞行中,让我们看看会发生什么:

查询在1秒后被取消了,没有点击 "取消"按钮。
爽啊!😊
这篇文章中的代码可以在CodeSandbox中找到:codesandbox.io/s/react-que…