Query Cancellation(查询取消)
TanStack Query 为每个查询函数提供了一个 AbortSignal
实例。当查询变得过时或不再活跃时,该信号将被触发中止。这意味着所有查询都是可取消的,并且如果需要,你可以在查询函数内部对取消作出响应。最棒的地方在于,这让你能够继续使用普通的 async/await
语法,同时享受自动取消的所有好处。
AbortController
API 在大多数运行时环境中可用,但如果你的运行时环境不支持它,则需要提供一个 polyfill(兼容补丁)。市面上有多个可用的 polyfill
。
默认行为
默认情况下,在 Promise
解析完成之前,卸载(unmount)或未使用的查询不会被取消。这意味着在 Promise
解析后,结果数据仍然会保留在缓存中。如果你已经发起了一个查询,但在其完成之前卸载了组件,那么如果再次挂载组件,并且查询尚未被垃圾回收,数据仍然可用。
然而,如果你使用了 AbortSignal
,则 Promise
将被取消(例如,中止 fetch
请求),因此,查询也必须被取消。取消查询后,其状态将恢复到之前的状态。
使用 fetch
const query = useQuery({
queryKey: ["todos"],
queryFn: async ({ signal }) => {
const todosResponse = await fetch("/todos", {
// 传递 signal 以支持取消
signal,
});
const todos = await todosResponse.json();
const todoDetails = todos.map(async ({ details }) => {
const response = await fetch(details, {
// 也可以传递到多个 fetch 请求中
signal,
});
return response.json();
});
return Promise.all(todoDetails);
},
});
使用 axios
(v0.22.0 及以上版本)
import axios from "axios";
const query = useQuery({
queryKey: ["todos"],
queryFn: ({ signal }) =>
axios.get("/todos", {
// 传递 signal 给 `axios`
signal,
}),
});
使用 axios(v0.22.0 以下版本)
import axios from "axios";
const query = useQuery({
queryKey: ["todos"],
queryFn: ({ signal }) => {
// 为请求创建一个新的 CancelToken
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
const promise = axios.get("/todos", {
// 传递 cancelToken
cancelToken: source.token,
});
// 如果 TanStack Query 触发中止,则取消请求
signal?.addEventListener("abort", () => {
source.cancel("Query was cancelled by TanStack Query");
});
return promise;
},
});
使用 XMLHttpRequest
const query = useQuery({
queryKey: ["todos"],
queryFn: ({ signal }) => {
return new Promise((resolve, reject) => {
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", () => {
resolve(JSON.parse(oReq.responseText));
});
signal?.addEventListener("abort", () => {
oReq.abort();
reject();
});
oReq.open("GET", "/todos");
oReq.send();
});
},
});
使用 graphql-request
AbortSignal
可以在 client.request
方法中设置:
const client = new GraphQLClient(endpoint);
const query = useQuery({
queryKey: ["todos"],
queryFn: ({ signal }) => {
client.request({ document: query, signal });
},
});
使用 graphql-request
(v4.0.0 以下版本)
AbortSignal
可以在 GraphQLClient
构造函数中设置:
const query = useQuery({
queryKey: ["todos"],
queryFn: ({ signal }) => {
const client = new GraphQLClient(endpoint, { signal });
return client.request(query, variables);
},
});
手动取消
在某些情况下,你可能希望手动取消查询。例如,如果请求耗时过长,你可以让用户点击“取消”按钮来终止请求。要实现此功能,只需调用 queryClient.cancelQueries({ queryKey })
,这将取消查询并将其状态恢复到之前的状态。如果你的查询函数使用了 signal
,TanStack Query 还会额外取消 Promise
。
const query = useQuery({
queryKey: ["todos"],
queryFn: async ({ signal }) => {
const resp = await fetch("/todos", { signal });
return resp.json();
},
});
const queryClient = useQueryClient();
return (
<button
onClick={(e) => {
e.preventDefault();
queryClient.cancelQueries({ queryKey: ["todos"] });
}}
>
Cancel
</button>
);
限制
取消功能在使用以下 Suspense hooks 时无效: