본문으로 건너뛰기

쿼리 취소

TanStack Query는 각 쿼리 함수에 AbortSignal 인스턴스를 제공합니다. 쿼리가 오래되거나 비활성 상태가 되면 이 signal은 중단됩니다. 즉, 모든 쿼리를 취소할 수 있으며 원하는 경우 쿼리 함수 내에서 취소에 대응할 수 있습니다. 가장 좋은 점은 자동 취소의 모든 이점을 누리면서도 일반적인 async/await 구문을 계속 사용할 수 있다는 것입니다.

AbortController API는 대부분의 런타임 환경에서 사용할 수 있지만, 런타임 환경에서 이를 지원하지 않으면 polyfill을 제공해야 합니다. 사용 가능한 polyfill이 여러 개 있습니다.

기본 동작

기본적으로 Promise가 이행되기 전에 마운트 해제되거나 사용되지 않게 된 쿼리는 취소되지 않습니다. 즉, Promise가 이행된 후 결과 데이터는 캐시에서 사용할 수 있습니다. 이는 쿼리를 받기 시작했지만 완료되기 전에 컴포넌트가 마운트 해제된 경우에 유용합니다. 컴포넌트를 다시 마운트했을 때 쿼리가 아직 가비지 컬렉션되지 않았다면 데이터를 사용할 수 있습니다.

하지만 AbortSignal을 사용하면 Promise가 취소되고(예: 가져오기가 중단됨), 따라서 Query도 취소해야 합니다. 쿼리를 취소하면 해당 상태가 이전 상태로 되돌아갑니다.

fetch 사용하기

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+ 사용하기

import axios from 'axios'

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

v0.22.0보다 낮은 버전의 axios 사용

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 사용하기

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 사용하기

클라이언트의 request 메서드에서 AbortSignal을 설정할 수 있습니다.

const client = new GraphQLClient(endpoint)

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

v4.0.0보다 낮은 버전의 graphql-request 사용

AbortSignalGraphQLClient 생성자에서 설정할 수 있습니다.

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>
)

Cancel Options

취소 옵션은 쿼리 취소 작업의 동작을 제어하는 데 사용됩니다.

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

취소 옵션 객체는 다음 속성을 지원합니다:

  • silent?: boolean
    • true로 설정하면 CancelledError가 옵저버(예: onError 콜백) 및 관련 알림으로 전파되는 것을 억제하고, 거부하는 대신 재시도 Promise를 반환합니다.
    • 기본값은 false입니다
  • revert?: boolean
    • true로 설정하면 쿼리의 상태(데이터 및 상태)를 진행 중인 가져오기 직전으로 복원하고, fetchStatus를 다시 idle로 설정하며, 이전 데이터가 없었던 경우에만 오류를 발생시킵니다.
    • 기본값은 true입니다

제한 사항

Suspense 훅인 useSuspenseQuery, useSuspenseQueriesuseSuspenseInfiniteQuery를 사용할 때는 취소가 작동하지 않습니다.