Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,10 @@
"label": "Window Focus Refetching",
"to": "framework/react/guides/window-focus-refetching"
},
{
"label": "Polling",
"to": "framework/react/guides/polling"
},
{
"label": "Disabling/Pausing Queries",
"to": "framework/react/guides/disabling-queries"
Expand Down Expand Up @@ -399,6 +403,10 @@
"label": "Window Focus Refetching",
"to": "framework/solid/guides/window-focus-refetching"
},
{
"label": "Polling",
"to": "framework/solid/guides/polling"
},
{
"label": "Disabling/Pausing Queries",
"to": "framework/solid/guides/disabling-queries"
Expand Down Expand Up @@ -536,6 +544,10 @@
"label": "Window Focus Refetching",
"to": "framework/vue/guides/window-focus-refetching"
},
{
"label": "Polling",
"to": "framework/vue/guides/polling"
},
{
"label": "Disabling/Pausing Queries",
"to": "framework/vue/guides/disabling-queries"
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/react/guides/important-defaults.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Out of the box, TanStack Query is configured with **aggressive but sane** defaul

> 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`.

- Queries can optionally be configured with a `refetchInterval` to trigger refetches periodically, which is independent of the `staleTime` setting.
- 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.

- 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.
- By default, "inactive" queries are garbage collected after **5 minutes**.
Expand Down
109 changes: 109 additions & 0 deletions docs/framework/react/guides/polling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
id: polling
title: Polling
---

`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:

[//]: # 'Example1'

```tsx
useQuery({
queryKey: ['prices'],
queryFn: fetchPrices,
refetchInterval: 5_000, // every 5 seconds
})
```

[//]: # 'Example1'

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.

## Adapting the interval to query state

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:

[//]: # 'Example2'

```tsx
useQuery({
queryKey: ['job', jobId],
queryFn: () => fetchJobStatus(jobId),
refetchInterval: (query) => {
// Stop polling once the job finishes
if (query.state.data?.status === 'complete') return false
return 2_000
},
})
```

[//]: # 'Example2'

Returning `false` clears the interval timer. If the query result changes so the function would return a positive number again, polling resumes automatically.

## Background polling

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:

[//]: # 'Example3'

```tsx
useQuery({
queryKey: ['portfolio'],
queryFn: fetchPortfolio,
refetchInterval: 30_000,
refetchIntervalInBackground: true,
})
```

[//]: # 'Example3'

## Pausing polling

Pass a function to `refetchInterval` and close over component state to control when polling runs:

[//]: # 'Example4'

```tsx
useQuery({
queryKey: ['prices', tokenAddress],
queryFn: () => fetchPrice(tokenAddress),
refetchInterval: () => {
if (!tokenAddress || isPaused) return false
return 15_000
},
})
```

[//]: # 'Example4'

## Polling with offline support

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:

[//]: # 'Example5'

```tsx
useQuery({
queryKey: ['chainStatus'],
queryFn: fetchChainStatus,
refetchInterval: 10_000,
networkMode: 'always',
})
```

[//]: # 'Example5'

For more on network modes, see [Network Mode](./network-mode.md).

## Note on deduplication

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.

[//]: # 'ReactNative'

## Non-browser environments

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.

[//]: # 'ReactNative'
73 changes: 73 additions & 0 deletions docs/framework/solid/guides/polling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
id: polling
title: Polling
ref: docs/framework/react/guides/polling.md
replace: { '@tanstack/react-query': '@tanstack/solid-query' }
---

[//]: # 'Example1'

```tsx
useQuery(() => ({
queryKey: ['prices'],
queryFn: fetchPrices,
refetchInterval: 5_000, // every 5 seconds
}))
```

[//]: # 'Example1'
[//]: # 'Example2'

```tsx
useQuery(() => ({
queryKey: ['job', jobId],
queryFn: () => fetchJobStatus(jobId),
refetchInterval: (query) => {
// Stop polling once the job finishes
if (query.state.data?.status === 'complete') return false
return 2_000
},
}))
```

[//]: # 'Example2'
[//]: # 'Example3'

```tsx
useQuery(() => ({
queryKey: ['portfolio'],
queryFn: fetchPortfolio,
refetchInterval: 30_000,
refetchIntervalInBackground: true,
}))
```

[//]: # 'Example3'
[//]: # 'Example4'

```tsx
useQuery(() => ({
queryKey: ['prices', tokenAddress],
queryFn: () => fetchPrice(tokenAddress),
refetchInterval: () => {
if (!tokenAddress || isPaused) return false
return 15_000
},
}))
```

[//]: # 'Example4'
[//]: # 'Example5'

```tsx
useQuery(() => ({
queryKey: ['chainStatus'],
queryFn: fetchChainStatus,
refetchInterval: 10_000,
networkMode: 'always',
}))
```

[//]: # 'Example5'
[//]: # 'ReactNative'
[//]: # 'ReactNative'
9 changes: 9 additions & 0 deletions docs/framework/vue/guides/polling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
id: polling
title: Polling
ref: docs/framework/react/guides/polling.md
replace: { '@tanstack/react-query': '@tanstack/vue-query' }
---

[//]: # 'ReactNative'
[//]: # 'ReactNative'
Loading