Skip to content

Commit 8a62ed1

Browse files
authored
Merge pull request #1436 from maruhgar/patch-1
2 parents 528ec1a + 6b962d2 commit 8a62ed1

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

docs/rtk-query/usage/pagination.mdx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,79 @@ const PostList = () => {
7676
};
7777
```
7878

79+
### Automated Re-fetching of Paginated Queries
80+
81+
It is a common use-case to utilize tag invalidation to perform
82+
[automated re-fetching](./automated-refetching.mdx) with RTK Query.
83+
84+
A potential pitfall when combining this with pagination is that your paginated query may only
85+
provide a _partial_ list at any given time, and hence not `provide` tags for entity IDs that
86+
fall on pages which aren't currently shown. If a specific entity is deleted that falls on an
87+
earlier page, the paginated query will not be providing a tag for that specific ID, and will
88+
not be invalidated to trigger re-fetching data. As a result, items on the current page that
89+
should shift one item up will not have done so, and the total count of items and/or pages
90+
may be incorrect.
91+
92+
A strategy to overcome this is to ensure that the `delete` mutation always `invalidates` the
93+
paginated query, even if the deleted item is not _currently_ provided on that page. We can
94+
leverage the concept of
95+
[advanced invalidation with abstract tag ids](./automated-refetching.mdx#advanced-invalidation-with-abstract-tag-ids)
96+
to do this by `providing` a `'Posts'` tag with the `'PARTIAL-LIST'` ID in our paginated query,
97+
and `invalidating` that corresponding tag for any mutation that should affect it.
98+
99+
```ts title="Example of invalidating cache for paginated queries"
100+
// file: api.ts
101+
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
102+
interface Post {
103+
id: number
104+
name: string
105+
}
106+
interface ListResponse<T> {
107+
page: number
108+
per_page: number
109+
total: number
110+
total_pages: number
111+
data: T[]
112+
}
113+
114+
export const postApi = createApi({
115+
reducerPath: 'postsApi',
116+
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
117+
tagTypes: ['Posts'],
118+
endpoints: (build) => ({
119+
listPosts: build.query<ListResponse<Post>, number | void>({
120+
query: (page = 1) => `posts?page=${page}`,
121+
// highlight-start
122+
providesTags: (result, error, page) =>
123+
result
124+
? [
125+
// Provides a tag for each post in the current page,
126+
// as well as the 'PARTIAL-LIST' tag.
127+
...result.data.map(({ id }) => ({ type: 'Posts' as const, id })),
128+
{ type: 'Posts', id: 'PARTIAL-LIST' },
129+
]
130+
: [{ type: 'Posts', id: 'PARTIAL-LIST' }],
131+
// highlight-end
132+
}),
133+
deletePost: build.mutation<{ success: boolean; id: number }, number>({
134+
query(id) {
135+
return {
136+
url: `post/${id}`,
137+
method: 'DELETE',
138+
}
139+
},
140+
// Invalidates the tag for this Post `id`, as well as the `PARTIAL-LIST` tag,
141+
// causing the `listPosts` query to re-fetch if a component is subscribed to the query.
142+
// highlight-start
143+
invalidatesTags: (result, error, id) => [
144+
{ type: 'Posts', id },
145+
{ type: 'Posts', id: 'PARTIAL-LIST' },
146+
],
147+
// highlight-end
148+
}),
149+
}),
150+
})
151+
```
79152
## General Pagination Example
80153

81154
In the following example, you'll see `Loading` on the initial query, but then as you move forward we'll use the next/previous buttons as a _fetching_ indicator while any non-cached query is performed. When you go back, the cached data will be served instantaneously.

0 commit comments

Comments
 (0)