Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

invalidateQueries should preserve refetch intent during initial fetch

オープン
#11,526 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
35/100
issue の種類
機能追加
明瞭さ
説明が足りない
活発さ
活発
技術スタック
typescript

調査の方向性

まず、初回フェッチ中の queryClient.invalidateQueries を追跡し、#8530 の以前の議論を確認します。繰り返しの無効化、末尾の refetch の動作、キャンセルについてテストを定義し、続いてグローバルポリシーと呼び出し単位の override をどのように指定するかを決定します。完了条件は、無効化の意図が失われず、選択したレイテンシーの動作がカバーされていることです。

索引モデルが issue の本文から書いたものです。

説明

AI-assisted issue: Written in GitHub Copilot, configured to GPT-5.6 Sol Medium, under close supervision of a frontend lead software engineer with 10+ years of experience. I spent roughly a full day reproducing this behavior and working back and forth with AI to understand the implementation, history, trade-offs, and alternatives. I therefore have high personal confidence in the core problem and proposed direction. Some API details may still need refinement, but I believe the substance is worth considering.

Problem

invalidateQueries() can effectively lose its refetch intent when called during an initial fetch.

The query is marked invalid, but the attempted refetch reuses the existing in-flight promise because no cached data exists yet. After that request settles, no trailing refetch occurs.

Data fetched from a server state predating the invalidation can therefore become the settled cache value indefinitely.

This was previously discussed in #8530. That discussion focused primarily on whether the initial request should be cancelled. I think cancellation and invalidation intent should be treated separately:

  • Cancellation determines how quickly the client reaches any usable state versus the final state.
  • Invalidation means the current result is no longer trusted and should eventually cause a fetch started after invalidation.
Real-world impact

We encountered this with a long-running data-check query:

  1. Query starts while server data is in state A.
  2. User changes server data to state B before query finishes.
  3. Mutation succeeds and invalidates the data-check query.
  4. Because the query is still performing its initial fetch, invalidation reuses that request.
  5. Original request returns a result based on state A.
  6. UI settles on that result even though backend is now in state B.

From user perspective, successful change appears not to have worked. UI contradicts current backend state solely because of request timing.

A loading indicator does not solve this. Loading ends when original request completes, but stale result remains cached without a subsequent request.

Expected invariant

Invalidation should be durable intent:

A fetch that started before an invalidation should not permanently satisfy that invalidation.

This does not necessarily mean initial fetch must be cancelled. Two useful strategies exist.

Strategy 1: first usable state sooner

Allow current fetch to complete, then perform one trailing refetch:

fetch A starts
invalidate
invalidate
fetch A completes and provides first usable state
fetch B starts once
fetch B completes with current state

Multiple invalidations should coalesce into one trailing refetch.

Benefits:

  • Same time-to-first-result as current behavior.
  • Avoids extending initial loading state.
  • Eventually converges to data fetched after invalidation.
  • Fits stale-while-revalidate behavior well.

This seems like a reasonable default.

Strategy 2: final state sooner

Cancel current fetch and immediately start a replacement:

fetch A starts
invalidate
fetch A is cancelled
fetch B starts immediately
fetch B completes with current state

Benefits:

  • Reaches current final state sooner.
  • Never commits known-outdated result.
  • Useful for status indicators and other correctness-sensitive queries.

Trade-off: user waits longer for any usable result.

Proposed API direction

Expose this as a global/defaultable query-client policy, with per-call override:

new QueryClient({
  defaultOptions: {
    queries: {
      invalidationBehavior: 'queue',
    },
  },
})

queryClient.invalidateQueries(filters, {
  invalidationBehavior: 'cancel',
})

Exact option and value names need design work. Names should communicate the latency choice:

  • Prioritize first usable state, then revalidate.
  • Prioritize final post-invalidation state.
  • Preserve current behavior, where the in-flight initial result is accepted without trailing work.

I do not have a good concise name for the third behavior yet. reuse and deduplicate describe implementation details rather than the freshness consequence.

I would avoid adding a none value here. refetchType: 'none' already represents mark-stale-without-refetch behavior.

Why both invariant and option matter

Automatically scheduling a trailing refetch fixes lost invalidation, but only provides the first-usable-state-sooner strategy.

Some applications need final-state-as-soon-as-possible behavior. They should not need to manually coordinate cancelQueries() followed by invalidateQueries() at every call site.

A global policy plus per-call override provides both:

  • Safe eventual-freshness default.
  • Explicit low-latency path to final state.
  • No ignored invalidation intent.
主要言語
TypeScript
スター
50.3k
フォーク
4.2k
平均マージ
22時間 33分
マージ済み PR(30日)
214

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

TanStack/query のほかの issue

TanStack/query の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。