Skip to content

Commit 29b8de7

Browse files
authored
Merge branch 'main' into fix/exclude-config-files-from-prod-dts
2 parents 668d758 + 2853bfb commit 29b8de7

150 files changed

Lines changed: 5525 additions & 3441 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,10 @@
12931293
{
12941294
"label": "Mutation Property Order",
12951295
"to": "eslint/mutation-property-order"
1296+
},
1297+
{
1298+
"label": "Prefer Query Options",
1299+
"to": "eslint/prefer-query-options"
12961300
}
12971301
]
12981302
},

docs/eslint/eslint-plugin-query.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,19 @@ export default [
4646
]
4747
```
4848

49+
### Recommended strict setup
50+
51+
The `flat/recommended-strict` config extends `flat/recommended` with additional opinionated rules that enforce best practices more aggressively.
52+
53+
```js
54+
import pluginQuery from '@tanstack/eslint-plugin-query'
55+
56+
export default [
57+
...pluginQuery.configs['flat/recommended-strict'],
58+
// Any other config...
59+
]
60+
```
61+
4962
### Custom setup
5063

5164
Alternatively, you can load the plugin and configure only the rules you want to use:
@@ -78,6 +91,16 @@ To enable all of the recommended rules for our plugin, add `plugin:@tanstack/que
7891
}
7992
```
8093

94+
### Recommended strict setup
95+
96+
The `recommendedStrict` config extends `recommended` with additional opinionated rules:
97+
98+
```json
99+
{
100+
"extends": ["plugin:@tanstack/query/recommendedStrict"]
101+
}
102+
```
103+
81104
### Custom setup
82105

83106
Alternatively, add `@tanstack/query` to the plugins section, and configure the rules you want to use:
@@ -100,3 +123,4 @@ Alternatively, add `@tanstack/query` to the plugins section, and configure the r
100123
- [@tanstack/query/infinite-query-property-order](./infinite-query-property-order.md)
101124
- [@tanstack/query/no-void-query-fn](./no-void-query-fn.md)
102125
- [@tanstack/query/mutation-property-order](./mutation-property-order.md)
126+
- [@tanstack/query/prefer-query-options](./prefer-query-options.md)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
---
2+
id: prefer-query-options
3+
title: Prefer the use of queryOptions
4+
---
5+
6+
Separating `queryKey` and `queryFn` can cause unexpected runtime issues when the same query key is accidentally used with more than one `queryFn`. Wrapping them in `queryOptions` (or `infiniteQueryOptions`) co-locates the key and function, making queries safer and easier to reuse.
7+
8+
## Rule Details
9+
10+
Examples of **incorrect** code for this rule:
11+
12+
```tsx
13+
/* eslint "@tanstack/query/prefer-query-options": "error" */
14+
15+
function Component({ id }) {
16+
const query = useQuery({
17+
queryKey: ['get', id],
18+
queryFn: () => Api.get(`/foo/${id}`),
19+
})
20+
// ...
21+
}
22+
```
23+
24+
```tsx
25+
/* eslint "@tanstack/query/prefer-query-options": "error" */
26+
27+
function useFooQuery(id) {
28+
return useQuery({
29+
queryKey: ['get', id],
30+
queryFn: () => Api.get(`/foo/${id}`),
31+
})
32+
}
33+
```
34+
35+
Examples of **correct** code for this rule:
36+
37+
```tsx
38+
/* eslint "@tanstack/query/prefer-query-options": "error" */
39+
40+
function getFooOptions(id) {
41+
return queryOptions({
42+
queryKey: ['get', id],
43+
queryFn: () => Api.get(`/foo/${id}`),
44+
})
45+
}
46+
47+
function Component({ id }) {
48+
const query = useQuery(getFooOptions(id))
49+
// ...
50+
}
51+
```
52+
53+
```tsx
54+
/* eslint "@tanstack/query/prefer-query-options": "error" */
55+
56+
function getFooOptions(id) {
57+
return queryOptions({
58+
queryKey: ['get', id],
59+
queryFn: () => Api.get(`/foo/${id}`),
60+
})
61+
}
62+
63+
function useFooQuery(id) {
64+
return useQuery({ ...getFooOptions(id), select: (data) => data.foo })
65+
}
66+
```
67+
68+
The rule also enforces reusing `queryKey` from a `queryOptions` result instead of typing it manually in `QueryClient` methods or filters.
69+
70+
Examples of **incorrect** `queryKey` references for this rule:
71+
72+
```tsx
73+
/* eslint "@tanstack/query/prefer-query-options": "error" */
74+
75+
function todoOptions(id) {
76+
return queryOptions({
77+
queryKey: ['todo', id],
78+
queryFn: () => api.getTodo(id),
79+
})
80+
}
81+
82+
function Component({ id }) {
83+
const queryClient = useQueryClient()
84+
return queryClient.getQueryData(['todo', id])
85+
}
86+
```
87+
88+
```tsx
89+
/* eslint "@tanstack/query/prefer-query-options": "error" */
90+
91+
function todoOptions(id) {
92+
return queryOptions({
93+
queryKey: ['todo', id],
94+
queryFn: () => api.getTodo(id),
95+
})
96+
}
97+
98+
function Component({ id }) {
99+
const queryClient = useQueryClient()
100+
return queryClient.invalidateQueries({ queryKey: ['todo', id] })
101+
}
102+
```
103+
104+
Examples of **correct** `queryKey` references for this rule:
105+
106+
```tsx
107+
/* eslint "@tanstack/query/prefer-query-options": "error" */
108+
109+
function todoOptions(id) {
110+
return queryOptions({
111+
queryKey: ['todo', id],
112+
queryFn: () => api.getTodo(id),
113+
})
114+
}
115+
116+
function Component({ id }) {
117+
const queryClient = useQueryClient()
118+
return queryClient.getQueryData(todoOptions(id).queryKey)
119+
}
120+
```
121+
122+
```tsx
123+
/* eslint "@tanstack/query/prefer-query-options": "error" */
124+
125+
function todoOptions(id) {
126+
return queryOptions({
127+
queryKey: ['todo', id],
128+
queryFn: () => api.getTodo(id),
129+
})
130+
}
131+
132+
function Component({ id }) {
133+
const queryClient = useQueryClient()
134+
return queryClient.invalidateQueries({ queryKey: todoOptions(id).queryKey })
135+
}
136+
```
137+
138+
## When Not To Use It
139+
140+
If you do not want to enforce the use of `queryOptions` in your codebase, you will not need this rule.
141+
142+
## Attributes
143+
144+
- [x] ✅ Recommended (strict)
145+
- [ ] 🔧 Fixable

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Out of the box, TanStack Query is configured with **aggressive but sane** defaul
1414
- set `staleTime` to `Infinity` to never trigger a refetch until the Query is [invalidated manually](./query-invalidation.md).
1515
- set `staleTime` to `'static'` to **never** trigger a refetch, even if the Query is [invalidated manually](./query-invalidation.md).
1616

17+
> `'static'` and `Infinity` both prevent staleness-based refetches, but `'static'` is stricter: `queryClient.invalidateQueries()` can invalidate a query with `staleTime: Infinity`, but has no effect on `staleTime: 'static'`. `refetchOnMount`, `refetchOnWindowFocus`, and `refetchOnReconnect` set to `"always"` are also blocked by `'static'`. Use `'static'` for data that cannot change while the app is running: feature flags fetched at boot, user permissions loaded at login, static reference tables. Use `Infinity` when you still want manual invalidation to work.
18+
1719
- Stale queries are refetched automatically in the background when:
1820
- New instances of the query mount
1921
- The window is refocused

examples/angular/auto-refetching/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.95.2",
16+
"@tanstack/angular-query-experimental": "^5.96.0",
1717
"rxjs": "^7.8.2",
1818
"tslib": "^2.8.1",
1919
"zone.js": "0.15.0"

examples/angular/basic-persister/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.95.2",
17-
"@tanstack/angular-query-persist-client": "^5.95.2",
18-
"@tanstack/query-async-storage-persister": "^5.95.2",
16+
"@tanstack/angular-query-experimental": "^5.96.0",
17+
"@tanstack/angular-query-persist-client": "^5.96.0",
18+
"@tanstack/query-async-storage-persister": "^5.96.0",
1919
"rxjs": "^7.8.2",
2020
"tslib": "^2.8.1",
2121
"zone.js": "0.15.0"

examples/angular/basic/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.95.2",
16+
"@tanstack/angular-query-experimental": "^5.96.0",
1717
"rxjs": "^7.8.2",
1818
"tslib": "^2.8.1",
1919
"zone.js": "0.15.0"

examples/angular/devtools-panel/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
1616
"@angular/router": "^20.0.0",
17-
"@tanstack/angular-query-experimental": "^5.95.2",
17+
"@tanstack/angular-query-experimental": "^5.96.0",
1818
"rxjs": "^7.8.2",
1919
"tslib": "^2.8.1",
2020
"zone.js": "0.15.0"

examples/angular/infinite-query-with-max-pages/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"@angular/compiler": "^20.0.0",
1414
"@angular/core": "^20.0.0",
1515
"@angular/platform-browser": "^20.0.0",
16-
"@tanstack/angular-query-experimental": "^5.95.2",
16+
"@tanstack/angular-query-experimental": "^5.96.0",
1717
"rxjs": "^7.8.2",
1818
"tslib": "^2.8.1",
1919
"zone.js": "0.15.0"

examples/angular/optimistic-updates/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"@angular/core": "^20.0.0",
1515
"@angular/forms": "^20.0.0",
1616
"@angular/platform-browser": "^20.0.0",
17-
"@tanstack/angular-query-experimental": "^5.95.2",
17+
"@tanstack/angular-query-experimental": "^5.96.0",
1818
"rxjs": "^7.8.2",
1919
"tslib": "^2.8.1",
2020
"zone.js": "0.15.0"

0 commit comments

Comments
 (0)