QueryClient.isFetching and useIsFetching accept a fetchStatus filter and ignore it
メンテナーはふだん 1 日以内に返信
@Apu-Emdad がすでに取り組んでいます。
2026年10月14日 から。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 78/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- react, typescript
調査の方向性
packages/query-core/src/queryClient.tsから始めます。issueで指摘されている、isFetching内のspreadとfetchStatusの上書きを確認し、続いてuseIsFetchingがフィルターをどのように渡すかを追います。QueryClient.isFetchingとuseIsFetchingに関連するテストを読み、報告されたpausedとidleのケースをカバーするテストを追加します。APIがフィルターを適用するか、型で受け付けないことを明示し、動作とドキュメントに一貫性があれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Describe the bug
QueryFilters has a fetchStatus?: FetchStatus field, and FetchStatus is 'fetching' | 'paused' | 'idle', so isFetching({ fetchStatus: 'paused' }) type-checks. It has no effect: QueryClient.isFetching spreads the filters it is given and then writes its own fetchStatus over them, so the argument is discarded for every value, not only 'paused'.
QueryCache.findAll honours the same filter correctly, which makes the difference easy to miss — the filter works through one entry point and is silently dropped through the other.
Minimal reproduction
import { QueryClient } from '@tanstack/query-core'
const client = new QueryClient()
const queryKey = ['session']
const cache = client.getQueryCache()
const query = cache.build(client, { queryKey, queryFn: async () => 'x' })
query.setState({ fetchStatus: 'paused' })
console.log(cache.find({ queryKey })?.state.fetchStatus) // 'paused'
console.log(cache.findAll({ queryKey, fetchStatus: 'paused' }).length) // 1
console.log(client.isFetching({ queryKey, fetchStatus: 'paused' })) // 0
console.log(client.isFetching({ queryKey, fetchStatus: 'idle' })) // 0
Expected / actual
| call | expected | actual |
|---|---|---|
findAll({ fetchStatus: 'paused' }).length |
1 | 1 |
isFetching({ fetchStatus: 'paused' }) |
1, or a type error | 0 |
isFetching({ fetchStatus: 'idle' }) |
1, or a type error | 0 |
Either outcome would be fine. What is awkward is a public, typed, documented parameter that is accepted and does nothing.
Cause
packages/query-core/src/queryClient.ts:
isFetching(filters) {
return this.#queryCache.findAll({ ...filters, fetchStatus: 'fetching' }).length
}
useIsFetching forwards its filters to this method, so it behaves the same way.
Why it came up
An app that asks "is this query still waiting on the network" wants paused to count: a lookup held because the browser is offline, or because the window is unfocused and the retry is deferred, has not answered yet. Reading it through useIsFetching({ queryKey, fetchStatus: 'paused' }) compiles and quietly answers 0, so the UI treats a waiting query as a settled one. In our case a page showed a "not found" state to someone whose session lookup was merely paused.
The workaround is to subscribe to the cache and use findAll directly, which works but means reimplementing what useIsFetching exists to provide.
Possible resolutions
In rough order of how little they change:
- Type it out — omit
fetchStatusfrom the filters these two accept, so passing it is a compile error, and say in the reference that they count fetching queries only. - Honour it — use the caller's
fetchStatuswhen one is given and default to'fetching'otherwise. This changes behaviour for anyone currently passing a value and relying on it being ignored, which seems unlikely but is a real semver consideration. - Leave the behaviour and document it — note on
useIsFetchingandQueryClient.isFetchingthatfetchStatusis fixed, and point atgetQueryCache().findAllfor other statuses.
Happy to send a PR for whichever you prefer.
Related
- Discussion #6756 proposes a built-in
queryClient.isPaused/useIsPaused, and its example hook uses the same spread-then-override pattern in user code. If that proposal is the intended answer, this is the part of it that bites today — and option 1 or 3 above would stop the current API misleading people in the meantime. - #7559 and #8531 are about
isFetchingreporting the wrong thing for fetching queries; this is narrower, about a filter argument being inert.
Environment
@tanstack/query-core5.104.1- Node 26.11.1, TypeScript 6.0.3
- Reproduced with
query-corealone, so no framework adapter is involved
- 主要言語
- TypeScript
- スター
- 50.4k
- フォーク
- 4.2k
- 平均マージ
- 8時間 37分
- マージ済み PR(30日)
- 417
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
TanStack/query のほかの issue
-
solid-query: STRICT_READ_UNTRACKED on Solid 2 — client()/options() read in component body (useMutation, useBaseQuery)再び着手できるかも @LeonxLJX が 41 日前に担当しましたが、オープン中のプルリクエストはありません。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
TanStack/query#11358 · コメント 2 件 · リアクション 1 件 ·
メンテナーはふだん 1 日以内に返信
-
solid-query: switching the queryClient accessor strands the new client's cache (subscription stays on the old observer)対応中かも @MaNaN1803 が 84 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
TanStack/query#11106 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
Explicit gcTime schedules a GC timer during SSR, retaining the whole render in memory対応中かも @AdzerKI が 1 日前に担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 25/100
メンテナーはふだん 1 日以内に返信
-
restoreQueries and persisterGc throw on malformed persisted entries対応中かも @VGontier-cmd が 9 日前に担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
TanStack/query#11903 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
似ている issue
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
dsh-market/dsh-market#877 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
stellar/stellar-dev-skill#151 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
メンテナーはふだん 1 日以内に返信
-
area:arcade game ready-for-agent
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
fderuiter/deruiter.dev#2281 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
scope: data grid type: bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
メンテナーはふだん 2 日以内に返信