译零 TransZero · TanStack Query v3 中文文档
主站

查询取消 query-cancellation

以前的方法需要一个cancel函数

React Query 为每个查询函数都提供了一个AbortSignal的实例,当且仅当你的运行环境支持该对象。 当一个查询变得过时或者不活跃,那么该“信号”将被中止。 这意味着,所有的查询都是可取消的,而且如果需要,您可以在查询函数中响应取消动作。 最好的是,您可以继续使用普通的 async/await 语法,同时获得自动取消的所有好处。 此外,这个解决方案比旧的解决方案更适合 TypeScript。

AbortController API 在大多数运行时环境中都是可用的。但是如果运行时环境不支持它,那么查询函数将在对应的地方接收到 undefined。 当然,你可以选择手动实现 AbortController API,在 npm 上有几个可用的。

注意:该特性在 3.30.0 版本中引入。 如果你仍在使用旧版本的,请尽快升级或者使用旧版本的 cancel 函数

默认行为

默认情况下,未挂载(unmount)的或者在其返回的 Promise 被执行之前的查询是不会取消的。 这意味着,在执行之后的数据是在缓存中可用。 因此,在已经开始接收一个查询时且在其完成之前,卸载其组件的情形是被允许的。并且若再次挂载组件,而查询还没有被 GC,那么数据是可用的。

但是,如果您使用了 AbortSignal 或者是附加了一个 cancel 函数,那么这个 Promise 是可以被取消的(例如,下面的取消 fetch 操作),因此查询也必须被取消。 取消查询将导致其状态恢复到以前的状态。

使用 fetch

JavaScript
1const query = useQuery('todos', async ({ signal }) => {2  const todosResponse = await fetch('/todos', {3    // 传递可撤销的信号到fetch里去4    signal,5  })6  const todos = await todosResponse.json()7 8  const todoDetails = todos.map(async ({ details } => {9    const response = await fetch(details, {10      // 或这将他传递给好几个实例11      signal,12    })13    return response.json()14  })15 16  return Promise.all(todoDetails)17})

使用 axios

使用 axios v0.22.0+

JavaScript
1import axios from "axios";2 3const query = useQuery("todos", ({ signal }) =>4  axios.get("/todos", {5    // 传递可撤销的信号到 `axios`6    signal,7  }),8);

使用 axios (版本低于 v0.22.0)

JavaScript
1import axios from "axios";2 3const query = useQuery("todos", ({ signal }) => {4  // 给这个 request 创建一个 CancelToken5  const CancelToken = axios.CancelToken;6  const source = CancelToken.source();7 8  const promise = axios.get("/todos", {9    // 传递这个 token 到该请求10    cancelToken: source.token,11  });12 13  // 如果 React Query 信号是 abort,那么撤销该请求14  signal?.addEventListener("abort", () => {15    source.cancel("Query was cancelled by React Query");16  });17 18  return promise;19});

使用 XMLHttpRequest

JavaScript
1const query = useQuery("todos", ({ signal }) => {2  return new Promise((resolve, reject) => {3    var oReq = new XMLHttpRequest();4    oReq.addEventListener("load", () => {5      resolve(JSON.parse(oReq.responseText));6    });7    signal?.addEventListener("abort", () => {8      oReq.abort();9      reject();10    });11    oReq.open("GET", "/todos");12    oReq.send();13  });14});

使用 graphql-request

使用 graphql-request v4.0.0+

可以传递给 request 函数一个 AbortSignal。

JavaScript
1const client = new GraphQLClient(endpoint);2 3const query = useQuery("todos", ({ signal }) => {4  client.request({ document: query, signal });5});

使用 graphql-request (版本低于 v4.0.0)

可以传递给 GraphQLClient 的构造函数一个 AbortSignal.

JavaScript
1const query = useQuery("todos", ({ signal }) => {2  const client = new GraphQLClient(endpoint, {3    signal,4  });5  return client.request(query, variables);6});

手动取消

有时,您可能会想手动取消。 例如,如果请求需要很长时间才能完成,此时允许用户单击“取消”按钮来停止请求。 为此,您只需要调用 queryClient.cancelQueries(key),取消此次查询并将数据还原到上一次的状态。 如果 promise.cancel 可用或者你在查询函数内处理了 signal,React Query 将取消该 Promise 的同时取消请求。

JavaScript
1const [queryKey] = useState("todos");2 3const query = useQuery(queryKey, async ({ signal }) => {4  const resp = await fetch("/todos", { signal });5  return resp.json();6});7 8const queryClient = useQueryClient();9 10return (11  <button12    onClick={(e) => {13      e.preventDefault();14      queryClient.cancelQueries(queryKey);15    }}16  >17    Cancel18  </button>19);

旧的 cancel 函数

不用担心!旧的取消功能设计将继续工作。 但是我们建议你从可取消的 Promise转移到新的 AbortSignal 接口,因为它已经被标准化为一个通用的构造,用于在大多数浏览器和 Node 中中止正在进行的活动。旧的取消函数可能会在未来的主版本中被删除。

要与此特性集成,请在(需要实现取消的查询)返回的 Promise 上附加一个 cancel 函数。 当查询变得过时或不活跃时,将调用 promise.cancel 函数(如果可用)。

使用 axios

JavaScript
1import axios from "axios";2 3const query = useQuery("todos", () => {4  // 为此请求创建一个新的 CancelToken 源5  const source = axios.CancelToken.source();6 7  const promise = axios.get("/todos", {8    // 传入该源到 cancelToken9    cancelToken: source.token,10  });11 12  // 如果 React Query 调用 `promise.cancel` 方法,则取消请求13  promise.cancel = () => {14    source.cancel("Query was cancelled by React Query");15  };16 17  return promise;18});

使用 fetch

JavaScript
1const query = useQuery("todos", () => {2  // 创建一个新的 AbortController 实例3  const controller = new AbortController();4  // 获取 abortController 的信号(signal)5  const signal = controller.signal;6 7  const promise = fetch("/todos", {8    method: "get",9    // 传入信号10    signal,11  });12 13  // 如果 React Query 调用 `promise.cancel` 方法,则取消请求14  promise.cancel = () => controller.abort();15 16  return promise;17});

本页为 TanStack Query 文档(React 版)的非官方中文翻译,原文由上游团队维护,遵循 MIT 许可。 译文如有偏差,以英文原文为准。

本页译文取自社区中文版 react-query-web-i18n(MIT),非本站翻译,已停止更新,内容可能与当前 API 有出入。

译自上游 (未记录版本)