Skip to content

Commit 67b12ae

Browse files
docs: add polling guide (#10330)
* docs(react): add polling guide Adds a dedicated guide for refetchInterval-based polling. The option was only mentioned in passing in important-defaults.md with no explanation of how it works, how to adapt it dynamically, or how it interacts with window focus, networkMode, and the enabled flag. Covers: - Basic setup and independence from staleTime - Dynamic intervals via function form - refetchIntervalInBackground for dashboards / always-on UIs - Disabling window-focus refetching for fullscreen game and kiosk UIs - Pausing polling with the enabled flag - networkMode: 'always' for unreliable navigator.onLine environments - Deduplication behavior across multiple observers Updates config.json to add the guide to the React sidebar between Window Focus Refetching and Disabling/Pausing Queries. Adds a cross-reference in important-defaults.md. * docs(react/polling): add example markers for framework portability * docs(react): fix deduplication note — timers are per observer, not per query * docs(react): address review feedback on polling guide - Remove staleTime enumeration; link to Important Defaults instead - Remove game/kiosk focus management examples (scope creep) - Rewrite pausing polling to use refetchInterval function instead of enabled: false - Fix offline support section: connectivity uses online/offline events, not navigator.onLine - Fix deduplication note: remove queuing implication and em dash - Add non-browser environments note pointing to React Native guide * docs(solid,vue): add polling guide framework ports * docs: wrap React Native section in markers for framework port exclusion * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 75052a7 commit 67b12ae

5 files changed

Lines changed: 204 additions & 1 deletion

File tree

docs/config.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@
246246
"label": "Window Focus Refetching",
247247
"to": "framework/react/guides/window-focus-refetching"
248248
},
249+
{
250+
"label": "Polling",
251+
"to": "framework/react/guides/polling"
252+
},
249253
{
250254
"label": "Disabling/Pausing Queries",
251255
"to": "framework/react/guides/disabling-queries"
@@ -399,6 +403,10 @@
399403
"label": "Window Focus Refetching",
400404
"to": "framework/solid/guides/window-focus-refetching"
401405
},
406+
{
407+
"label": "Polling",
408+
"to": "framework/solid/guides/polling"
409+
},
402410
{
403411
"label": "Disabling/Pausing Queries",
404412
"to": "framework/solid/guides/disabling-queries"
@@ -536,6 +544,10 @@
536544
"label": "Window Focus Refetching",
537545
"to": "framework/vue/guides/window-focus-refetching"
538546
},
547+
{
548+
"label": "Polling",
549+
"to": "framework/vue/guides/polling"
550+
},
539551
{
540552
"label": "Disabling/Pausing Queries",
541553
"to": "framework/vue/guides/disabling-queries"

docs/framework/react/guides/important-defaults.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Out of the box, TanStack Query is configured with **aggressive but sane** defaul
2323

2424
> Setting `staleTime` is the recommended way to avoid excessive refetches, but you can also customize the points in time for refetches by setting options like `refetchOnMount`, `refetchOnWindowFocus` and `refetchOnReconnect`.
2525
26-
- Queries can optionally be configured with a `refetchInterval` to trigger refetches periodically, which is independent of the `staleTime` setting.
26+
- Queries can optionally be configured with a `refetchInterval` to trigger refetches periodically, which is independent of the `staleTime` setting. See [Polling](./polling.md) for details.
2727

2828
- Query results that have no more active instances of `useQuery`, `useInfiniteQuery` or query observers are labeled as "inactive" and remain in the cache in case they are used again at a later time.
2929
- By default, "inactive" queries are garbage collected after **5 minutes**.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
---
2+
id: polling
3+
title: Polling
4+
---
5+
6+
`refetchInterval` makes a query refetch on a timer. Set it to a number in milliseconds and the query runs every N ms while there's at least one active observer:
7+
8+
[//]: # 'Example1'
9+
10+
```tsx
11+
useQuery({
12+
queryKey: ['prices'],
13+
queryFn: fetchPrices,
14+
refetchInterval: 5_000, // every 5 seconds
15+
})
16+
```
17+
18+
[//]: # 'Example1'
19+
20+
Polling is independent of `staleTime`. A query can be fresh and still poll on schedule; see [Important Defaults](./important-defaults.md) for how `staleTime` interacts with other refetch behaviors. `refetchInterval` fires on its own clock regardless of freshness.
21+
22+
## Adapting the interval to query state
23+
24+
Pass a function instead of a number to compute the interval from the current query. The function receives the `Query` object and should return a number in ms or `false` to stop polling:
25+
26+
[//]: # 'Example2'
27+
28+
```tsx
29+
useQuery({
30+
queryKey: ['job', jobId],
31+
queryFn: () => fetchJobStatus(jobId),
32+
refetchInterval: (query) => {
33+
// Stop polling once the job finishes
34+
if (query.state.data?.status === 'complete') return false
35+
return 2_000
36+
},
37+
})
38+
```
39+
40+
[//]: # 'Example2'
41+
42+
Returning `false` clears the interval timer. If the query result changes so the function would return a positive number again, polling resumes automatically.
43+
44+
## Background polling
45+
46+
By default, polling pauses when the browser tab loses focus. For dashboards or any interface where data needs to stay current even while the user is in another tab, disable that behavior:
47+
48+
[//]: # 'Example3'
49+
50+
```tsx
51+
useQuery({
52+
queryKey: ['portfolio'],
53+
queryFn: fetchPortfolio,
54+
refetchInterval: 30_000,
55+
refetchIntervalInBackground: true,
56+
})
57+
```
58+
59+
[//]: # 'Example3'
60+
61+
## Pausing polling
62+
63+
Pass a function to `refetchInterval` and close over component state to control when polling runs:
64+
65+
[//]: # 'Example4'
66+
67+
```tsx
68+
useQuery({
69+
queryKey: ['prices', tokenAddress],
70+
queryFn: () => fetchPrice(tokenAddress),
71+
refetchInterval: () => {
72+
if (!tokenAddress || isPaused) return false
73+
return 15_000
74+
},
75+
})
76+
```
77+
78+
[//]: # 'Example4'
79+
80+
## Polling with offline support
81+
82+
TanStack Query detects connectivity by listening to the browser's `online` and `offline` events. In environments where those events don't fire reliably (Electron, some embedded WebViews), set `networkMode: 'always'` to skip the connectivity check:
83+
84+
[//]: # 'Example5'
85+
86+
```tsx
87+
useQuery({
88+
queryKey: ['chainStatus'],
89+
queryFn: fetchChainStatus,
90+
refetchInterval: 10_000,
91+
networkMode: 'always',
92+
})
93+
```
94+
95+
[//]: # 'Example5'
96+
97+
For more on network modes, see [Network Mode](./network-mode.md).
98+
99+
## Note on deduplication
100+
101+
Each `QueryObserver` (each component using `useQuery` with `refetchInterval`) runs its own timer. Two components subscribed to the same key with `refetchInterval: 5000` each fire their timer every 5 seconds. What gets deduplicated is concurrent in-flight fetches: if two timers fire at the same time, only one network request goes out. The timers are observer-level; the deduplication is query-level.
102+
103+
[//]: # 'ReactNative'
104+
105+
## Non-browser environments
106+
107+
For non-browser runtimes like React Native, the standard `online`/`offline` and focus events aren't available. The [React Native guide](../react-native.md) covers how to connect `focusManager` and `onlineManager` to native app state APIs.
108+
109+
[//]: # 'ReactNative'
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
---
2+
id: polling
3+
title: Polling
4+
ref: docs/framework/react/guides/polling.md
5+
replace: { '@tanstack/react-query': '@tanstack/solid-query' }
6+
---
7+
8+
[//]: # 'Example1'
9+
10+
```tsx
11+
useQuery(() => ({
12+
queryKey: ['prices'],
13+
queryFn: fetchPrices,
14+
refetchInterval: 5_000, // every 5 seconds
15+
}))
16+
```
17+
18+
[//]: # 'Example1'
19+
[//]: # 'Example2'
20+
21+
```tsx
22+
useQuery(() => ({
23+
queryKey: ['job', jobId],
24+
queryFn: () => fetchJobStatus(jobId),
25+
refetchInterval: (query) => {
26+
// Stop polling once the job finishes
27+
if (query.state.data?.status === 'complete') return false
28+
return 2_000
29+
},
30+
}))
31+
```
32+
33+
[//]: # 'Example2'
34+
[//]: # 'Example3'
35+
36+
```tsx
37+
useQuery(() => ({
38+
queryKey: ['portfolio'],
39+
queryFn: fetchPortfolio,
40+
refetchInterval: 30_000,
41+
refetchIntervalInBackground: true,
42+
}))
43+
```
44+
45+
[//]: # 'Example3'
46+
[//]: # 'Example4'
47+
48+
```tsx
49+
useQuery(() => ({
50+
queryKey: ['prices', tokenAddress],
51+
queryFn: () => fetchPrice(tokenAddress),
52+
refetchInterval: () => {
53+
if (!tokenAddress || isPaused) return false
54+
return 15_000
55+
},
56+
}))
57+
```
58+
59+
[//]: # 'Example4'
60+
[//]: # 'Example5'
61+
62+
```tsx
63+
useQuery(() => ({
64+
queryKey: ['chainStatus'],
65+
queryFn: fetchChainStatus,
66+
refetchInterval: 10_000,
67+
networkMode: 'always',
68+
}))
69+
```
70+
71+
[//]: # 'Example5'
72+
[//]: # 'ReactNative'
73+
[//]: # 'ReactNative'
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
id: polling
3+
title: Polling
4+
ref: docs/framework/react/guides/polling.md
5+
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
6+
---
7+
8+
[//]: # 'ReactNative'
9+
[//]: # 'ReactNative'

0 commit comments

Comments
 (0)