[TanStakQuery] Query Cancellation

Jeris·2023년 5월 22일
0

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

AbortController API는 대부분의 런타임 환경에서 사용할 수 있지만, 런타임 환경이 이 API를 지원하지 않는 경우 쿼리 함수가 undefined를 받게 됩니다. 원하는 경우 AbortController API를 polyfill하도록 선택할 수 있으며, 여러 가지 방법을 사용할 수 있습니다.

Default behavior

기본적으로, 프로미스가 해결되기 전에 언마운트되거나 사용되지 않는 쿼리는 취소되지 않습니다. 즉, 프로미스가 해결된 후에도 결과 데이터를 캐시에서 사용할 수 있습니다. 이 기능은 쿼리 수신을 시작했지만 완료되기 전에 컴포넌트를 언마운트하는 경우에 유용합니다. 컴포넌트를 리마운트하고 쿼리가 아직 가비지 수집되지 않은 경우 데이터를 사용할 수 있습니다.

그러나 AbortSignal을 소모하면(consume) 프로미스가 취소되므로(예: aborting the fetch) 쿼리도 취소해야 합니다. 쿼리를 취소하면 상태가 이전 상태로 되돌아갑니다.

Using 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)
  },
})

Using axios v0.22.0+

import axios from 'axios'

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

Using `axios with version lower than v0.22.0

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

Using 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()
    })
  },
})

Using graphql-request

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

const client = new GraphQLClient(endpoint)

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

Using graphql-request with version lower than v4.0.0

GraphQLClient constructor에서 AbortSignal을 설정할 수 있습니다.

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

Manual Cancellation

You might want to cancel a query manually. For example, if the request takes a long time to finish, you can allow the user to click a cancel button to stop the request. To do this, you just need to call queryClient.cancelQueries({ queryKey }), which will cancel the query and revert it back to its previous state. If you have consumed the signal passed to the query function, TanStack Query will additionally also cancel the Promise.
쿼리를 수동으로 취소하고 싶을 수도 있습니다. 예를 들어 request를 완료하는 데 시간이 오래 걸리는 경우 사용자가 취소 버튼을 클릭하여 request를 중지하도록 허용할 수 있습니다. 이렇게 하려면 쿼리를 취소하고 이전 상태로 되돌리기 위해 queryClient.cancelQueries({ queryKey })를 호출하기만 하면 됩니다. 쿼리 함수에 전달된 신호를 소모한 경우, TanStack Query는 추가로 프로미스도 취소합니다.

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

Reference

profile
job's done

0개의 댓글