← 返回 Skill 列表
extension
分类: 开发与工程API Key 暂未确认

tanstack-pacer

TanStack Pacer在React中的执行控制最佳实践——包括防抖、节流、速率限制、队列和批处理。适用于实现搜索输入、滚动处理器、API速率限制、任务队列、批量操作或任何需要与响应式状态结合的受控执行时间的场景。

person作者: jakexiaohubgithub

TanStack Pacer

Version: @tanstack/react-pacer@latest Requires: React 16.8+, TypeScript recommended

Quick Setup

npm install @tanstack/react-pacer
import { useDebouncedCallback } from '@tanstack/react-pacer'

function SearchInput() {
  const debouncedSearch = useDebouncedCallback(
    (query: string) => fetchResults(query),
    { wait: 300 },
  )

  return <input onChange={(e) => debouncedSearch(e.target.value)} />
}

4 Hook Variants Per Utility

Each utility (Debouncer, Throttler, RateLimiter, Queuer, Batcher) provides 4 React hooks:

| Hook | Returns | Use Case | |------|---------|----------| | use[Utility] | Instance | Full control, custom state subscriptions | | use[Utility]Callback | Wrapped function | Simple event handler wrapping | | use[Utility]State | [value, setValue, instance] | Debounced/throttled React state | | use[Utility]Value | [derivedValue, instance] | Read-only derived value from props/state |

PacerProvider (Optional)

import { PacerProvider } from '@tanstack/react-pacer'

<PacerProvider
  defaultOptions={{
    debouncer: { wait: 300 },
    throttler: { wait: 200 },
  }}
>
  <App />
</PacerProvider>

Devtools

npm install -D @tanstack/react-devtools @tanstack/react-pacer-devtools
import { TanStackDevtools } from '@tanstack/react-devtools'
import { pacerDevtoolsPlugin } from '@tanstack/react-pacer-devtools'

<TanStackDevtools plugins={[pacerDevtoolsPlugin()]} />

Utilities must have a key option to appear in devtools.

Rule Categories

| Priority | Category | Rule File | Impact | |----------|----------|-----------|--------| | CRITICAL | Hook Selection | rules/hook-selection.md | Correct hook choice per use case | | CRITICAL | Debouncing | rules/deb-debouncing.md | Prevents wasted API calls and flickering UI | | HIGH | Throttling | rules/thr-throttling.md | Smooth, evenly-spaced execution | | HIGH | State & Reactivity | rules/state-reactivity.md | Prevents unnecessary re-renders | | HIGH | Async Patterns | rules/async-patterns.md | Correct async execution with retry, abort, error handling | | MEDIUM | Rate Limiting | rules/rl-rate-limiting.md | Enforces execution budgets | | MEDIUM | Queuing | rules/que-queuing.md | Lossless ordered/priority task processing | | MEDIUM | Batching | rules/bat-batching.md | Groups operations for bulk processing | | LOW | Configuration | rules/config-options.md | Dynamic options, providers, shared config | | LOW | Devtools | rules/devtools.md | Debugging and monitoring utilities |

Critical Rules

Always Do

  • Use hooks over function wrappers — useDebouncedCallback not debounce() for proper React lifecycle
  • Choose the right hook variant — useCallback for event handlers, useState for controlled inputs, useValue for derived values
  • Opt-in to state subscriptions — pass selector as 3rd arg: useDebouncer(fn, opts, (s) => ({ isPending: s.isPending }))
  • Use async variants for API calls — useAsyncDebouncedCallback gives error handling, retry, abort
  • Pass AbortSignal to fetch — getAbortSignal() enables cancellation of in-flight requests
  • Use key option for devtools — only keyed utilities appear in devtools panel

Never Do

  • Subscribe to all state — omit selector or use instance directly to avoid re-renders on every state change
  • Use debouncing when you need guaranteed execution — use throttling or queuing instead
  • Use rate limiting for evenly-spaced calls — rate limiting is bursty; use throttling for smooth spacing
  • Use debounce() function in React — no lifecycle cleanup; use useDebouncedCallback hook instead
  • Expect maxWait on Debouncer — Pacer has no maxWait; use Throttler for guaranteed periodic execution

Key Patterns

// Debounced search input with state
import { useDebouncedState } from '@tanstack/react-pacer'

function Search() {
  const [query, setQuery, debouncer] = useDebouncedState('', { wait: 300 })
  // query updates after 300ms pause; setQuery is immediate
  return <input onChange={(e) => setQuery(e.target.value)} />
}

// Async debounced API call with abort
import { useAsyncDebouncedCallback } from '@tanstack/react-pacer'

function AsyncSearch() {
  const search = useAsyncDebouncedCallback(
    async (query: string) => {
      const signal = search.getAbortSignal()
      const res = await fetch(`/api/search?q=${query}`, { signal })
      return res.json()
    },
    { wait: 300, onSuccess: (data) => setResults(data) },
  )
  return <input onChange={(e) => search(e.target.value)} />
}

// Throttled scroll handler
import { useThrottledCallback } from '@tanstack/react-pacer'

function ScrollTracker() {
  const onScroll = useThrottledCallback(
    () => trackScrollPosition(window.scrollY),
    { wait: 100 },
  )
  useEffect(() => {
    window.addEventListener('scroll', onScroll)
    return () => window.removeEventListener('scroll', onScroll)
  }, [onScroll])
}

// Derived debounced value from props
import { useDebouncedValue } from '@tanstack/react-pacer'

function FilteredList({ filter }: { filter: string }) {
  const [debouncedFilter] = useDebouncedValue(filter, { wait: 300 })
  return <ExpensiveList filter={debouncedFilter} />
}

// Async queue with concurrency
import { useAsyncQueuer } from '@tanstack/react-pacer'

function UploadQueue() {
  const queuer = useAsyncQueuer(uploadFile, { concurrency: 3 })
  return <button onClick={() => queuer.addItem(file)}>Upload</button>
}