Skip to content

Commit 9cef1ea

Browse files
authored
feat: add query function context (#1261)
1 parent fca1f5a commit 9cef1ea

File tree

24 files changed

+191
-173
lines changed

24 files changed

+191
-173
lines changed

docs/src/pages/guides/infinite-queries.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ With this information, we can create a "Load More" UI by:
4343
import { useInfiniteQuery } from 'react-query'
4444

4545
function Projects() {
46-
const fetchProjects = (key, cursor = 0) =>
47-
fetch('/api/projects?cursor=' + cursor)
46+
const fetchProjects = ({ pageParam = 0 }) =>
47+
fetch('/api/projects?cursor=' + pageParam)
4848

4949
const {
5050
data,
@@ -98,8 +98,8 @@ By default, the variable returned from `getNextPageParam` will be supplied to th
9898

9999
```js
100100
function Projects() {
101-
const fetchProjects = (key, cursor = 0) =>
102-
fetch('/api/projects?cursor=' + cursor)
101+
const fetchProjects = ({ pageParam = 0 }) =>
102+
fetch('/api/projects?cursor=' + pageParam)
103103

104104
const {
105105
status,

docs/src/pages/guides/migrating-to-react-query-3.md

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,32 @@ const queryClient = new QueryClient({
6565
})
6666
```
6767

68+
### Query function parameters
69+
70+
Query functions now get a `QueryFunctionContext` instead of the query key parameters.
71+
72+
The `QueryFunctionContext` contains a `queryKey` and a `pageParam` in case of ininite queries.
73+
74+
useQuery:
75+
76+
```js
77+
// Old
78+
useQuery(['post', id], (_key, id) => fetchPost(id))
79+
80+
// New
81+
useQuery(['post', id], () => fetchPost(id))
82+
```
83+
84+
useInfiniteQuery:
85+
86+
```js
87+
// Old
88+
useInfiniteQuery(['posts'], (_key, pageParam = 0) => fetchPosts(pageParam))
89+
90+
// New
91+
useInfiniteQuery(['posts'], ({ pageParam = 0 }) => fetchPost(pageParam))
92+
```
93+
6894
### usePaginatedQuery()
6995

7096
The `usePaginatedQuery()` hook has been replaced by the `keepPreviousData` option on `useQuery`:
@@ -93,9 +119,13 @@ const {
93119
fetchNextPage,
94120
hasNextPage,
95121
isFetchingNextPage,
96-
} = useInfiniteQuery('projects', fetchProjects, {
97-
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
98-
})
122+
} = useInfiniteQuery(
123+
'projects',
124+
({ pageParam = 0 }) => fetchProjects(pageParam),
125+
{
126+
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
127+
}
128+
)
99129
```
100130

101131
Both directions:
@@ -109,10 +139,14 @@ const {
109139
hasPreviousPage,
110140
isFetchingNextPage,
111141
isFetchingPreviousPage,
112-
} = useInfiniteQuery('projects', fetchProjects, {
113-
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
114-
getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,
115-
})
142+
} = useInfiniteQuery(
143+
'projects',
144+
({ pageParam = 0 }) => fetchProjects(pageParam),
145+
{
146+
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
147+
getPreviousPageParam: (firstPage, pages) => firstPage.prevCursor,
148+
}
149+
)
116150
```
117151

118152
One direction reversed:
@@ -123,13 +157,17 @@ const {
123157
fetchNextPage,
124158
hasNextPage,
125159
isFetchingNextPage,
126-
} = useInfiniteQuery('projects', fetchProjects, {
127-
select: data => ({
128-
pages: [...data.pages].reverse(),
129-
pageParams: [...data.pageParams].reverse(),
130-
}),
131-
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
132-
})
160+
} = useInfiniteQuery(
161+
'projects',
162+
({ pageParam = 0 }) => fetchProjects(pageParam),
163+
{
164+
select: data => ({
165+
pages: [...data.pages].reverse(),
166+
pageParams: [...data.pageParams].reverse(),
167+
}),
168+
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
169+
}
170+
)
133171
```
134172

135173
Manually removing the first page:
@@ -276,6 +314,12 @@ The `forceFetchOnMount` query option has been replaced by `refetchOnMount: 'alwa
276314
When `refetchOnMount` was set to `false` any additional components were prevented from refetching on mount.
277315
In version 3 only the component where the option has been set will not refetch on mount.
278316
317+
### QueryOptions.queryFnParamsFilter
318+
319+
The `queryFnParamsFilter` option has been removed because query functions now get a `QueryFunctionContext` object instead of the query key.
320+
321+
Parameters can still be filtered within the query function itself as the `QueryFunctionContext` also contains the query key.
322+
279323
### QueryResult.clear()
280324
281325
The `QueryResult.clear()` method has been renamed to `QueryResult.remove()`.

docs/src/pages/guides/paginated-queries.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Consider the following example where we would ideally want to increment a pageIn
2727
function Todos() {
2828
const [page, setPage] = React.useState(0)
2929

30-
const fetchProjects = (key, page = 0) => fetch('/api/projects?page=' + page)
30+
const fetchProjects = (page = 0) => fetch('/api/projects?page=' + page)
3131

3232
const {
3333
isLoading,
@@ -36,7 +36,7 @@ function Todos() {
3636
data,
3737
isFetching,
3838
isPreviousData,
39-
} = useQuery(['projects', page], fetchProjects)
39+
} = useQuery(['projects', page], () => fetchProjects(page))
4040

4141
return (
4242
<div>

docs/src/pages/guides/query-functions.md

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,31 +46,17 @@ useQuery(['todos', todoId], async () => {
4646

4747
## Query Function Variables
4848

49-
Query keys are not just for uniquely identifying the data you are fetching, but are also conveniently passed as variables for your query function and while not always necessary, this makes it possible to extract your query functions if needed. The individual parts of the query key get passed through to your query function as parameters in the same order they appear in the array key:
49+
Query keys are not just for uniquely identifying the data you are fetching, but are also conveniently passed into your query function and while not always necessary, this makes it possible to extract your query functions if needed:
5050

5151
```js
5252
function Todos({ completed }) {
5353
const result = useQuery(['todos', { status, page }], fetchTodoList)
5454
}
5555

5656
// Access the key, status and page variables in your query function!
57-
function fetchTodoList(key, { status, page }) {
57+
function fetchTodoList({ queryKey }) {
58+
const { status, page } = queryKey[1]
5859
return new Promise()
59-
// ...
60-
}
61-
```
62-
63-
If you send through more items in your query key, they will also be available in your query function:
64-
65-
```js
66-
function Todo({ todoId, preview }) {
67-
const result = useQuery(['todo', todoId, { preview }], fetchTodoById)
68-
}
69-
70-
// Access status and page in your query function!
71-
function fetchTodoById(key, todoId, { preview }) {
72-
return new Promise()
73-
// ...
7460
}
7561
```
7662

docs/src/pages/reference/useInfiniteQuery.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ title: useInfiniteQuery
44
---
55

66
```js
7-
8-
const queryFn = (...queryKey, pageParam) // => Promise
9-
107
const {
118
fetchNextPage,
129
fetchPreviousPage,
@@ -15,17 +12,24 @@ const {
1512
isFetchingNextPage,
1613
isFetchingPreviousPage,
1714
...result
18-
} = useInfiniteQuery(queryKey, queryFn, {
15+
} = useInfiniteQuery(queryKey, ({ pageParam = 1 }) => fetchPage(pageParam), {
1916
...options,
2017
getNextPageParam: (lastPage, allPages) => lastPage.nextCursor,
21-
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor
18+
getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
2219
})
2320
```
2421

2522
**Options**
2623

2724
The options for `useInfiniteQuery` are identical to the [`useQuery` hook](#usequery) with the addition of the following:
2825

26+
- `queryFn: (context: QueryFunctionContext) => Promise<TData>`
27+
- **Required, but only if no default query function has been defined**
28+
- The function that the query will use to request data.
29+
- Receives a `QueryFunctionContext` object with the following variables:
30+
- `queryKey: QueryKey`
31+
- `pageParam: unknown | undefined`
32+
- Must return a promise that will either resolves data or throws an error.
2933
- `getNextPageParam: (lastPage, allPages) => unknown | undefined`
3034
- 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.
3135
- It should return a **single variable** that will be passed as the last optional parameter to your query function.

docs/src/pages/reference/useQuery.md

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ const {
3232
onError,
3333
onSettled,
3434
onSuccess,
35-
queryFnParamsFilter,
3635
queryKeyHashFn,
3736
refetchInterval,
3837
refetchIntervalInBackground,
@@ -63,11 +62,11 @@ const result = useQuery({
6362
- The query key to use for this query.
6463
- The query key will be hashed into a stable hash. See [Query Keys](./guides/query-keys) for more information.
6564
- The query will automatically update when this key changes (as long as `enabled` is not set to `false`).
66-
- `queryFn: (...params: unknown[]) => Promise<TData>`
65+
- `queryFn: (context: QueryFunctionContext) => Promise<TData>`
6766
- **Required, but only if no default query function has been defined**
6867
- The function that the query will use to request data.
69-
- Receives the following variables in the order that they are provided:
70-
- Query Key parameters
68+
- Receives a `QueryFunctionContext` object with the following variables:
69+
- `queryKey: QueryKey`
7170
- Must return a promise that will either resolves data or throws an error.
7271
- `enabled: boolean`
7372
- Set this to `false` to disable this query from automatically running.
@@ -149,10 +148,6 @@ const result = useQuery({
149148
- Optional
150149
- Defaults to `false`
151150
- If set, any previous `data` will be kept when fetching new data because the query key changed.
152-
- `queryFnParamsFilter: (...params: unknown[]) => unknown[]`
153-
- Optional
154-
- This function will filter the params that get passed to `queryFn`.
155-
- For example, you can filter out the first query key from the params by using `queryFnParamsFilter: params => params.slice(1)`.
156151
- `structuralSharing: boolean`
157152
- Optional
158153
- Defaults to `true`
@@ -174,6 +169,10 @@ const result = useQuery({
174169
- A derived boolean from the `status` variable above, provided for convenience.
175170
- `isError: boolean`
176171
- A derived boolean from the `status` variable above, provided for convenience.
172+
- `isLoadingError: boolean`
173+
- Will be `true` if the query failed while fetching for the first time.
174+
- `isRefetchError: boolean`
175+
- Will be `true` if the query failed while refetching.
177176
- `data: TData`
178177
- Defaults to `undefined`.
179178
- The last successfully resolved data for the query.

examples/basic/src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,15 @@ function Posts({ setPostId }) {
9090
);
9191
}
9292

93-
const getPostById = async (key, id) => {
93+
const getPostById = async (id) => {
9494
const { data } = await axios.get(
9595
`https://jsonplaceholder.typicode.com/posts/${id}`
9696
);
9797
return data;
9898
};
9999

100100
function usePost(postId) {
101-
return useQuery(["post", postId], getPostById, {
101+
return useQuery(["post", postId], () => getPostById(postId), {
102102
enabled: !!postId,
103103
});
104104
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { useQuery } from "react-query";
22
import axios from "axios";
33

4-
const getPostById = async (_, postId) => {
4+
const getPostById = async (postId) => {
55
const { data } = await axios.get(
66
`https://jsonplaceholder.typicode.com/posts/${postId}`
77
);
88
return data;
99
};
1010

1111
export default function usePost(postId) {
12-
return useQuery(["post", postId], getPostById);
12+
return useQuery(["post", postId], () => getPostById(postId));
1313
}

examples/default-query-function/src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ import {
1111
import { ReactQueryDevtools } from "react-query-devtools";
1212

1313
// Define a default query function that will receive the query key
14-
const defaultQueryFn = async (key) => {
14+
const defaultQueryFn = async ({ queryKey }) => {
1515
const { data } = await axios.get(
16-
`https://jsonplaceholder.typicode.com${key}`
16+
`https://jsonplaceholder.typicode.com${queryKey[0]}`
1717
);
1818
return data;
1919
};

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ function Example() {
3030
hasNextPage,
3131
} = useInfiniteQuery(
3232
'projects',
33-
async (_key, nextId = 0) => {
34-
const res = await axios.get('/api/projects?cursor=' + nextId)
33+
async ({ pageParam = 0 }) => {
34+
const res = await axios.get('/api/projects?cursor=' + pageParam)
3535
return res.data
3636
},
3737
{

0 commit comments

Comments
 (0)