第165章:ウィンドウフォーカスで自動更新
この章では、TanStack Query の「タブを切り替えて戻ってきたら自動で最新データに更新してくれる」機能を体験します🔄 これ、地味だけどめちゃ便利です…!🥹💕
1) どういう機能?🤔💡
ブラウザで別タブを見て、またアプリのタブに戻った瞬間に…
- もし キャッシュが古い(stale) なら 👉 自動で refetch(再取得)
- 設定で オフにもできる
- さらに 毎回必ず更新(always) もできる
という仕組みです✨
「戻ったら最新になってる」体験が、ほぼ設定だけで作れます🪄
(refetchOnWindowFocus のデフォルトは true です)(TanStack)
2) 図でイメージしよ〜🧠🗺️(Mermaid)
ポイントはここ👇
✅ true のときは「stale なら refetch」(TanStack)
✅ always は「stale じゃなくても毎回 refetch」(TanStack)
3) まずは体験!フォーカスで数字が変わるデモ🎲🔄
✅ FocusRefetchDemo.tsx を作ろう
src/FocusRefetchDemo.tsx を作って、これを貼ってね👇
import { useQuery } from '@tanstack/react-query'
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
type FocusData = {
time: string
random: number
}
async function fetchFocusData(): Promise<FocusData> {
await sleep(500) // 通信っぽくする演出⏳
return {
time: new Date().toLocaleTimeString(),
random: Math.random(),
}
}
export function FocusRefetchDemo() {
const query = useQuery({
queryKey: ['focus-demo'],
queryFn: fetchFocusData,
// 第165章の主役✨
refetchOnWindowFocus: true,
// ここが超大事:0だと「すぐ stale」になりやすい
staleTime: 0,
})
return (
<section style={{ padding: 16, border: '1px solid #ccc', borderRadius: 12 }}>
<h2 style={{ marginTop: 0 }}>フォーカス自動更新デモ👀✨</h2>
{query.isPending ? (
<p>読み込み中…⏳</p>
) : query.isError ? (
<p>エラーだよ…😭</p>
) : (
<>
<p>取得した時刻:{query.data.time} 🕒</p>
<p>ランダム:{query.data.random.toFixed(5)} 🎲</p>
</>
)}
<p style={{ marginTop: 12, fontSize: 12, opacity: 0.8 }}>
dataUpdatedAt:{new Date(query.dataUpdatedAt).toLocaleTimeString()} 🔄
</p>
<p style={{ fontSize: 12, opacity: 0.8 }}>
👉 別タブへ → 戻る、でランダムが変わったら成功!
</p>
</section>
)
}
✅ App.tsx に置こう
import { FocusRefetchDemo } from './FocusRefetchDemo'
export default function App() {
return (
<main style={{ padding: 24 }}>
<FocusRefetchDemo />
</main>
)
}
✅ 動かし方🎮
npm run devで起動🚀- ブラウザで 別タブへ移動
- またアプリのタブへ戻る
- ランダム値が変わったら「フォーカスで refetch」成功🎉
4) 「戻っても更新されない…」の理由ランキング🧐💦
🥇 データが stale じゃない(まだ新鮮)
refetchOnWindowFocus: true は「stale のときだけ」更新します(TanStack)
なので、staleTime を長くしてると「戻っても更新されない」ことがあります🙂
例:10秒は新鮮扱いにする👇
staleTime: 10_000
🥈 refetchOnWindowFocus: false にしてた
オフなら当然更新されません🙅♀️(TanStack)
🥉 そもそもそのクエリが画面にいない(非アクティブ)
コンポーネントがアンマウントされてると、そもそも「戻った瞬間に更新」が見えません💡
5) 設定パターン3つ🌈(ここ超よく使う!)
A) ページによってオフにする🙅♀️
「更新いらない」「通信重い」みたいな画面に👍
useQuery({
queryKey: ['xxx'],
queryFn,
refetchOnWindowFocus: false,
})
B) 戻ってきたら毎回必ず更新したい🔁
「株価」「チャット一覧」「監視ダッシュボード」みたいなやつに強い!
useQuery({
queryKey: ['xxx'],
queryFn,
refetchOnWindowFocus: 'always',
})
(always も公式に用意されてます)(TanStack)
C) “条件付き”で更新したい🧠✨
「30秒以上たってたら更新」みたいな賢いルール!
useQuery({
queryKey: ['xxx'],
queryFn,
refetchOnWindowFocus: (query) => {
// 30秒以上前のデータなら、フォーカスで更新する
return Date.now() - query.state.dataUpdatedAt > 30_000
},
})
(関数もOKです)(TanStack)
6) アプリ全体で一括設定(QueryClient)🧰✨
「基本はオフにしたい」みたいなときはこれが便利!
main.tsx(または QueryClient 作ってる場所)で👇(TanStack)
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false, // 全体でオフ
},
},
})
逆に「全体は true、特定の画面だけ false」みたいに 上書きもできます👍
7) ミニ課題🎯(5〜10分でOK✨)
staleTime: 0のまま → タブ戻るたび更新されるのを確認🔄🎲staleTime: 10_000にして → 10秒以内は更新されにくいのを確認⏱️refetchOnWindowFocus: 'always'にして → 毎回更新されるのを確認🔁- “30秒ルール”の関数版にして → 条件付きで動くのを確認🧠✨
次の章(第166章)は、この仕組みを「記事詳細ページ」みたいな実戦っぽい画面に入れて、キャッシュの効き方を体験していく感じだよ〜📚🔥