Skip to content

Commit 9be63f4

Browse files
TkDodoecyrbeDamianOsipiuk
authored
feat: better infinite queries
* feat: remove manual mode for infinite queries * refactor: simplify checking for next / previous fetch * types: better typings for fetchMeta.direction * refactor: fix hasNextPage / hasPreviousPage we should always return a boolean here, and according to the docs and the implementation in infiniteQueryBehaviour, we will only stop fetching if we return `undefined` from `getNextPageParam` or `getPreviousPageParam`. The checks for `false` or `null` on this boolean were likely wrong * fix: hasNextPage / hasPreviousPage is now always a boolean * feat: defaultPageParam * types: defaultPageParam is mandatory * fix: we also need `defaultPageParam` for `fetchInfiniteQuery` now * test: fix some assertions that relied on `undefined` being in `pageParams` * add some failing tests to show the problem with pageParam typings * feat: add PageParam typing (#5005) * feat: add PageParam typing * feat(infinite-query): more typing * feat(infinite-query): make pageParam never by defaukt * feat(infinite-query): PageParam type unknown for infinite queries * fix(infinite-query): fix tests * feat(infinite-query): add previous end next return TPageParam type * revert changes to pnpm-lock.yaml * types: add TPageParam to other framework adapters * fix(vue-query): correct generic typing * types: rename InfiniteQueryOptions to InfiniteQueryPageParamsOptions because it might be mistaken for a full set of options, which it is not * types: add failing select tests * fix formatting (new prettier version on v5) * types: add types for FetchMeta everywhere * feat(select): defer TData inference (#5040) * feat(select): defer TData inference * test(select): restore test * fix(select): change InfiniteQuery Observer definition * fix(infinite-queries): fix solid type inference select * fix(infinite-queries): fix solid type inference select * chore(infinite-queries): fix prettier * fix(infinite-queries): fix vue and svelte types * fix(infinite-queries): fix react type inference select * chore(infinite-queries): fix imports * style: fix prettier warnings * docs: docs for new infinite query features * docs: examples use new syntax --------- Co-authored-by: ecyrbe <ecyrbe@gmail.com> Co-authored-by: Damian Osipiuk <osipiukd+git@gmail.com>
1 parent e19d1fb commit 9be63f4

39 files changed

+749
-931
lines changed

docs/react/guides/infinite-queries.md

Lines changed: 10 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -103,42 +103,11 @@ function Projects() {
103103

104104
When an infinite query becomes `stale` and needs to be refetched, each group is fetched `sequentially`, starting from the first one. This ensures that even if the underlying data is mutated, we're not using stale cursors and potentially getting duplicates or skipping records. If an infinite query's results are ever removed from the queryCache, the pagination restarts at the initial state with only the initial group being requested.
105105

106-
## What if I need to pass custom information to my query function?
107-
108-
By default, the variable returned from `getNextPageParam` will be supplied to the query function, but in some cases, you may want to override this. You can pass custom variables to the `fetchNextPage` function which will override the default variable like so:
109-
110-
[//]: # 'Example3'
111-
112-
```tsx
113-
function Projects() {
114-
const fetchProjects = ({ pageParam = 0 }) =>
115-
fetch('/api/projects?cursor=' + pageParam)
116-
117-
const {
118-
status,
119-
data,
120-
isFetching,
121-
isFetchingNextPage,
122-
fetchNextPage,
123-
hasNextPage,
124-
} = useInfiniteQuery({
125-
queryKey: ['projects'],
126-
queryFn: fetchProjects,
127-
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
128-
})
129-
130-
// Pass your own page param
131-
const skipToCursor50 = () => fetchNextPage({ pageParam: 50 })
132-
}
133-
```
134-
135-
[//]: # 'Example3'
136-
137106
## What if I want to implement a bi-directional infinite list?
138107

139108
Bi-directional lists can be implemented by using the `getPreviousPageParam`, `fetchPreviousPage`, `hasPreviousPage` and `isFetchingPreviousPage` properties and functions.
140109

141-
[//]: # 'Example4'
110+
[//]: # 'Example3'
142111

143112
```tsx
144113
useInfiniteQuery({
@@ -149,13 +118,13 @@ useInfiniteQuery({
149118
})
150119
```
151120

152-
[//]: # 'Example4'
121+
[//]: # 'Example3'
153122

154123
## What if I want to show the pages in reversed order?
155124

156125
Sometimes you may want to show the pages in reversed order. If this is case, you can use the `select` option:
157126

158-
[//]: # 'Example5'
127+
[//]: # 'Example4'
159128

160129
```tsx
161130
useInfiniteQuery({
@@ -168,13 +137,13 @@ useInfiniteQuery({
168137
})
169138
```
170139

171-
[//]: # 'Example5'
140+
[//]: # 'Example4'
172141

173142
## What if I want to manually update the infinite query?
174143

175144
Manually removing first page:
176145

177-
[//]: # 'Example6'
146+
[//]: # 'Example5'
178147

179148
```tsx
180149
queryClient.setQueryData(['projects'], (data) => ({
@@ -183,11 +152,11 @@ queryClient.setQueryData(['projects'], (data) => ({
183152
}))
184153
```
185154

186-
[//]: # 'Example6'
155+
[//]: # 'Example5'
187156

188157
Manually removing a single value from an individual page:
189158

190-
[//]: # 'Example7'
159+
[//]: # 'Example6'
191160

192161
```tsx
193162
const newPagesArray =
@@ -201,11 +170,11 @@ queryClient.setQueryData(['projects'], (data) => ({
201170
}))
202171
```
203172

204-
[//]: # 'Example7'
173+
[//]: # 'Example6'
205174

206175
Make sure to keep the same data structure of pages and pageParams!
207176

208-
[//]: # 'Example8'
177+
[//]: # 'Example7'
209178

210179
## What if I want to limit the number of pages?
211180

@@ -219,7 +188,7 @@ This is made possible by using the `maxPages` option in conjunction with `getNex
219188

220189
In the following example only 3 pages are kept in the query data pages array. If a refetch is needed, only 3 pages will be refetched sequentially.
221190

222-
[//]: # 'Example9'
191+
[//]: # 'Example7'
223192

224193
```tsx
225194
useInfiniteQuery({

docs/react/guides/migrating-to-v5.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ This in turn will enable other frameworks to have the same functionality in a fr
248248
import { queryClient } from './my-client'
249249

250250
const { data } = useQuery(
251-
{
251+
{
252252
queryKey: ['users', id],
253253
queryFn: () => fetch(...),
254254
- context: customContext
@@ -265,6 +265,26 @@ However, refetching all pages might lead to UI inconsistencies. Also, this optio
265265

266266
The v5 includes a new `maxPages` option for infinite queries to limit the number of pages to store in the query data and to refetch. This new feature handles the use cases initially identified for the `refetchPage` page feature without the related issues.
267267

268+
### Infinite queries now need a `defaultPageParam`
269+
270+
Previously, we've passed `undefined` to the `queryFn` as `pageParam`, and you could assign a default value to the `pageParam` parameter in the `queryFn` function signature. This had the drawback of storing `undefined` in the `queryCache`, which is not serializable.
271+
272+
Instead, you now have to pass an explicit `defaultPageParam` to the infinite query options. This will be used as the `pageParam` for the first page:
273+
274+
```diff
275+
useInfiniteQuery({
276+
queryKey,
277+
- queryFn: ({ pageParam = 0 }) => fetchSomething(pageParam),
278+
+ queryFn: ({ pageParam }) => fetchSomething(pageParam),
279+
+ defaultPageParam: 0,
280+
getNextPageParam: (lastPage) => lastPage.next,
281+
})
282+
```
283+
284+
### Manual mode for infinite queries has been removed
285+
286+
Previously, we've allowed to overwrite the `pageParams` that would be returned from `getNextPageParam` or `getPreviousPageParam` by passing a `pageParam` value directly to `fetchNextPage` or `fetchPreviousPage`. This feature didn't work at all with refetches and wasn't widely known or used. This also means that `getNextPagParam` is now required for infinite queries.
287+
268288
[//]: # 'FrameworkBreakingChanges'
269289

270290
## React Query Breaking Changes

docs/react/reference/useInfiniteQuery.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ const {
1414
...result
1515
} = useInfiniteQuery({
1616
queryKey,
17-
queryFn: ({ pageParam = 1 }) => fetchPage(pageParam),
17+
queryFn: ({ pageParam }) => fetchPage(pageParam),
18+
defaultPageParam: 1,
1819
...options,
1920
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
2021
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
@@ -30,12 +31,15 @@ The options for `useInfiniteQuery` are identical to the [`useQuery` hook](../ref
3031
- The function that the query will use to request data.
3132
- Receives a [QueryFunctionContext](../guides/query-functions#queryfunctioncontext)
3233
- Must return a promise that will either resolve data or throw an error.
33-
- Make sure you return the data *and* the `pageParam` if needed for use in the props below.
34-
- `getNextPageParam: (lastPage, allPages) => unknown | undefined`
34+
- `defaultPageParam: TPageParam`
35+
- **Required**
36+
- The default page param to use when fetching the first page.
37+
- `getNextPageParam: (lastPage, allPages) => TPageParam | undefined`
38+
- **Required**
3539
- When new data is received for this query, this function receives both the last page of the infinite list of data and the full array of all pages.
3640
- It should return a **single variable** that will be passed as the last optional parameter to your query function.
3741
- Return `undefined` to indicate there is no next page available.
38-
- `getPreviousPageParam: (firstPage, allPages) => unknown | undefined`
42+
- `getPreviousPageParam: (firstPage, allPages) => TPageParam | undefined`
3943
- When new data is received for this query, this function receives both the first page of the infinite list of data and the full array of all pages.
4044
- It should return a **single variable** that will be passed as the last optional parameter to your query function.
4145
- Return `undefined` to indicate there is no previous page available.

docs/vue/guides/infinite-queries.md

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,24 +54,7 @@ const {
5454
```
5555

5656
[//]: # 'Example'
57-
[//]: # 'Example3'
58-
59-
```tsx
60-
const fetchProjects = ({ pageParam = 0 }) =>
61-
fetch('/api/projects?cursor=' + pageParam)
62-
63-
const { fetchNextPage } = useInfiniteQuery({
64-
queryKey: ['projects'],
65-
queryFn: fetchProjects,
66-
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
67-
})
68-
69-
// Pass your own page param
70-
const skipToCursor50 = () => fetchNextPage({ pageParam: 50 })
71-
```
72-
73-
[//]: # 'Example3'
74-
[//]: # 'Example7'
57+
[//]: # 'Example6'
7558

7659
```tsx
7760
const newPagesArray =
@@ -85,4 +68,4 @@ queryClient.setQueryData(['projects'], (data) => ({
8568
}))
8669
```
8770

88-
[//]: # 'Example7'
71+
[//]: # 'Example6'

examples/react/algolia/src/algolia.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ type SearchOptions = {
1616
export async function search<TData>({
1717
indexName,
1818
query,
19-
pageParam = 0,
19+
pageParam,
2020
hitsPerPage = 10,
2121
}: SearchOptions): Promise<{
2222
hits: Hit<TData>[];

examples/react/algolia/src/useAlgolia.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default function useAlgolia<TData>({
2222
queryKey: ["algolia", indexName, query, hitsPerPage],
2323
queryFn: ({ pageParam }) =>
2424
search<TData>({ indexName, query, pageParam, hitsPerPage }),
25+
defaultPageParam: 0,
2526
getNextPageParam: (lastPage) => lastPage?.nextPage,
2627
staleTime,
2728
cacheTime,

examples/react/infinite-query-with-max-pages/pages/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,11 @@ function Example() {
3131
hasPreviousPage,
3232
} = useInfiniteQuery({
3333
queryKey: ['projects'],
34-
queryFn: async ({ pageParam = 0 }) => {
34+
queryFn: async ({ pageParam }) => {
3535
const res = await axios.get('/api/projects?cursor=' + pageParam)
3636
return res.data
3737
},
38+
defaultPageParam: 0,
3839
getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
3940
getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
4041
maxPages: 3,

examples/react/load-more-infinite-scroll/pages/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ function Example() {
3535
hasPreviousPage,
3636
} = useInfiniteQuery({
3737
queryKey: ['projects'],
38-
queryFn: async ({ pageParam = 0 }) => {
38+
queryFn: async ({ pageParam }) => {
3939
const res = await axios.get('/api/projects?cursor=' + pageParam)
4040
return res.data
4141
},
42+
defaultPageParam: 0,
4243
getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
4344
getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
4445
})

0 commit comments

Comments
 (0)