use-cancelable-query

基于 TanStack Query 的可取消请求 hook,适用于 TanStack Start/Router 生态

适用于 TanStack Query v5+ / TanStack Start / TanStack Router

生态选择

生态Hook
Next.js + SWRuse-cancelable-swr
TanStack Start/Routeruse-cancelable-query(本页)

这是什么?

痛点:使用 TanStack Query 时,需要手动处理请求取消、类似 SWR 的 mutate API、以及路由 Loader 预取集成。

方案:封装 useQuery,提供统一的可取消请求接口,并增加类似 SWR 的 mutate 方法,同时支持 TanStack Router 的 Loader 预取。

安装与使用

npx shadcn@latest add https://ssp-ui-registry-staging.sh3.agoralab.co/r/use-cancelable-query.json
yarn dlx shadcn@latest add https://ssp-ui-registry-staging.sh3.agoralab.co/r/use-cancelable-query.json
pnpx shadcn@latest add https://ssp-ui-registry-staging.sh3.agoralab.co/r/use-cancelable-query.json
bunx shadcn@latest add https://ssp-ui-registry-staging.sh3.agoralab.co/r/use-cancelable-query.json

基础用法

import { useCancelableQuery } from '@/hook/use-cancelable-query'

function ProductList() {
  const [{ data, isLoading, error, mutate }] = useCancelableQuery<Product[]>({
    url: '/api/products',
    key: ['products']
  })

  if (isLoading) return <p>Loading...</p>
  if (error) return <p>Error: {error.message}</p>

  return (
    <div>
      <button onClick={() => mutate()}>刷新数据</button>
      <ul>
        {data?.map((product) => (
          <li key={product.id}>{product.name}</li>
        ))}
      </ul>
    </div>
  )
}

Mutate API

const [{ mutate }] = useCancelableQuery({ url: '/api/data', key: ['data'] })

// 仅重新验证
await mutate()

// 设置数据并重新验证
await mutate(newData)

// 设置数据但不重新验证
await mutate(newData, { revalidate: false })

// 使用 updater 函数
await mutate((old) => [...old, newItem])

TanStack Router 集成

在 Loader 中预取数据,与组件共享缓存:

// src/routes/products/index.tsx
import { createFileRoute } from '@tanstack/react-router'
import { prefetchQuery, useCancelableQuery } from '@/hook/use-cancelable-query'

export const Route = createFileRoute('/products')({
  loader: async ({ context }) => {
    await prefetchQuery(context.queryClient, {
      key: ['products'],
      url: '/api/products'
    })
  },
  component: ProductsPage
})

function ProductsPage() {
  const [{ data }] = useCancelableQuery<Product[]>({
    url: '/api/products',
    key: ['products']
  })
  return <ProductList products={data ?? []} />
}

globalMutate

在组件外部或跨组件使 query 失效:

import { globalMutate } from '@/hook/use-cancelable-query'

// 使所有以 'products' 开头的 query 失效
globalMutate(queryClient, (key) => Array.isArray(key) && key[0] === 'products')

目录