框架
版本

查询取消

TanStack Query 为每个查询函数提供一个 AbortSignal 实例。当一个查询过期或变得不活跃时,此 信号 将被中止。这意味着所有查询都可以被取消,并且您可以选择在查询函数内部响应取消。最棒的是,这允许您继续使用正常的 async/await 语法,同时获得自动取消的所有好处。

AbortController API 在 大多数运行时环境 中可用,但如果您的运行时环境不支持,您需要提供一个 polyfill。有 几个可用的

默认行为

默认情况下,在 Promise 解析之前未挂载或变得不使用的查询不会被取消。这意味着在 Promise 解析后,结果数据将在缓存中可用。如果您开始接收一个查询,但组件在完成之前被卸载,这一点很有用。如果您再次挂载组件并且查询尚未被垃圾回收,数据将可用。

但是,如果您使用了 AbortSignal,Promise 将被取消(例如,中止 fetch),因此 Query 也必须被取消。取消查询将导致其状态恢复到其先前状态。

使用 fetch

tsx
const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const todosResponse = await fetch('/todos', {
      // Pass the signal to one fetch
      signal,
    })
    const todos = await todosResponse.json()

    const todoDetails = todos.map(async ({ details }) => {
      const response = await fetch(details, {
        // Or pass it to several
        signal,
      })
      return response.json()
    })

    return Promise.all(todoDetails)
  },
})
const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const todosResponse = await fetch('/todos', {
      // Pass the signal to one fetch
      signal,
    })
    const todos = await todosResponse.json()

    const todoDetails = todos.map(async ({ details }) => {
      const response = await fetch(details, {
        // Or pass it to several
        signal,
      })
      return response.json()
    })

    return Promise.all(todoDetails)
  },
})

使用 axios v0.22.0+

tsx
import axios from 'axios'

const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: ({ signal }) =>
    axios.get('/todos', {
      // Pass the signal to `axios`
      signal,
    }),
})
import axios from 'axios'

const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: ({ signal }) =>
    axios.get('/todos', {
      // Pass the signal to `axios`
      signal,
    }),
})

使用版本低于 v0.22.0 的 axios

tsx
import axios from 'axios'

const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: ({ signal }) => {
    // Create a new CancelToken source for this request
    const CancelToken = axios.CancelToken
    const source = CancelToken.source()

    const promise = axios.get('/todos', {
      // Pass the source token to your request
      cancelToken: source.token,
    })

    // Cancel the request if TanStack Query signals to abort
    signal?.addEventListener('abort', () => {
      source.cancel('Query was cancelled by TanStack Query')
    })

    return promise
  },
})
import axios from 'axios'

const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: ({ signal }) => {
    // Create a new CancelToken source for this request
    const CancelToken = axios.CancelToken
    const source = CancelToken.source()

    const promise = axios.get('/todos', {
      // Pass the source token to your request
      cancelToken: source.token,
    })

    // Cancel the request if TanStack Query signals to abort
    signal?.addEventListener('abort', () => {
      source.cancel('Query was cancelled by TanStack Query')
    })

    return promise
  },
})

使用 XMLHttpRequest

tsx
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()
    })
  },
})
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 可以在客户端 request 方法中设置。

tsx
const client = new GraphQLClient(endpoint)

const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: ({ signal }) => {
    client.request({ document: query, signal })
  },
})
const client = new GraphQLClient(endpoint)

const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: ({ signal }) => {
    client.request({ document: query, signal })
  },
})

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

一个 AbortSignal 可以在 GraphQLClient 构造函数中设置。

tsx
const query = useQuery(() => {
  queryKey: ['todos'],
  queryFn: ({ signal }) => {
    const client = new GraphQLClient(endpoint, {
      signal,
    })
    return client.request(query, variables)
  },
})
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。

ts
const query = useQuery({
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const resp = await fetch('/todos', { signal })
    return resp.json()
  },
})

const queryClient = useQueryClient()

function onButtonClick() {
  queryClient.cancelQueries({ queryKey: ['todos'] })
}
const query = useQuery({
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const resp = await fetch('/todos', { signal })
    return resp.json()
  },
})

const queryClient = useQueryClient()

function onButtonClick() {
  queryClient.cancelQueries({ queryKey: ['todos'] })
}

取消选项

取消选项用于控制查询取消操作的行为。

tsx
// Cancel specific queries silently
await queryClient.cancelQueries({ queryKey: ['posts'] }, { silent: true })
// Cancel specific queries silently
await queryClient.cancelQueries({ queryKey: ['posts'] }, { silent: true })

取消选项对象支持以下属性

  • silent?: boolean
    • 当设置为 true 时,会抑制 CancelledError 向观察者(例如 onError 回调)的传播以及相关的通知,并返回重试 Promise 而不是拒绝。
    • 默认为 false
  • revert?: boolean
    • 当设置为 true 时,会从正在进行的 fetch 之前立即恢复查询的状态(数据和状态),将 fetchStatus 设置回 idle,并且仅在没有先前数据时抛出。
    • 默认为 true

限制

在使用 Suspense hooks 时,取消不起作用:useSuspenseQueryuseSuspenseQueriesuseSuspenseInfiniteQuery