SuperAPI AI Directory

tanstack-query-intent-core-cancel-queries-and-consume-a-19e3bf0b

General & other

View repository

Updated 10-16 00:02

About this skill

Setup

import { useQuery } from '@tanstack/react-query'

export function useTodo(todoId: string) {
  return useQuery({
    queryKey: ['todo', todoId],
    queryFn: async ({ signal }) => {
      const response = await fetch(`/api/todos/${todoId}`, { signal })
      return response.json() as Promise<{ id: string; title: string }>
    },
  })
}

Core Patterns

Cancel before optimistic writes

import { QueryClient } from '@tanstack/react-query'

const queryClient = new QueryClient()

export async function prepareTodoWrite() {
  await queryClient.cancelQueries({ queryKey: ['todos'] })
}

Consume one signal across nested fetches

import { queryOptions } from '@tanstack/react-query'

export const todosWithDetailsOptions = queryOptions({
  queryKey: ['todos-with-details'],
  queryFn: async ({ signal }) => {
    const todos = await fetch('/api/todos', { signal }).then(
      (response) => response.json() as Promise<Array<{ id: string }>>,
    )
    return Promise.all(
      todos.map((todo) =>
        fetch(`/api/todos/${todo.id}`, { signal }).then((response) =>
          response.json(),
        ),
      ),
    )
  },
})

Cancel by query key

import { QueryClient } from '@tanstack/react-query'

const queryClient = new QueryClient()

export function cancelTodos() {
  return queryClient.cancelQueries({ queryKey: ['todos'] })
}

Common Mistakes

HIGH Ignoring AbortSignal

Wrong:

import { useQuery } from '@tanstack/react-query'

export function useTodo(id: string) {
  return useQuery({
    queryKey: ['todo', id],
    queryFn: async () => fetch(`/api/todos/${id}`).then((r) => r.json()),
  })
}

Correct:

import { useQuery } from '@tanstack/react-query'

export function useTodo(id: string) {
  return useQuery({
    queryKey: ['todo', id],
    queryFn: async ({ signal }) =>
      fetch(`/api/todos/${id}`, { signal }).then((r) => r.json()),
  })
}

TanStack Query provides an AbortSignal; the request is only cancelled if the query function consumes it.

Source: TanStack/query:docs/framework/react/guides/query-cancellation.md

HIGH Assuming unmount cancels

Wrong:

import { useQuery } from '@tanstack/react-query'

export function useReport() {
  return useQuery({
    queryKey: ['report'],
    queryFn: async () => fetch('/api/report').then((r) => r.json()),
  })
}

Correct:

import { useQuery } from '@tanstack/react-query'

export function useReport() {
  return useQuery({
    queryKey: ['report'],
    queryFn: async ({ signal }) =>
      fetch('/api/report', { signal }).then((r) => r.json()),
  })
}

Unused queries can continue and populate cache unless the signal is consumed.

Source: TanStack/query:docs/framework/react/guides/query-cancellation.md

HIGH Suspense cancellation expected

Wrong:

import { useSuspenseQuery } from '@tanstack/react-query'

export function useTodo(id: string) {
  return useSuspenseQuery({
    queryKey: ['todo', id],
    queryFn: async ({ signal }) =>
      fetch(`/api/todos/${id}`, { signal }).then((r) => r.json()),
  })
}

Correct:

import { useQuery } from '@tanstack/react-query'

export function useTodo(id: string) {
  return useQuery({
    queryKey: ['todo', id],
    queryFn: async ({ signal }) =>
      fetch(`/api/todos/${id}`, { signal }).then((r) => r.json()),
  })
}

Cancellation limitations apply to Suspense hooks; use non-suspense queries when cancellation behavior is required.

Source: TanStack/query:docs/framework/react/guides/query-cancellation.md