Skip to content

Commit

Permalink
Add initial WIP infinite query docs
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Jan 17, 2025
1 parent 7a2ee50 commit 40568c1
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/rtk-query/api/createApi.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,9 @@ The `infiniteQueryOptions` field includes:
- `getNextPageParam`: a required callback you must provide to calculate the next page param, given the existing cached pages and page params
- `getPreviousPageParam`: an optional callback that will be used to calculate the previous page param, if you try to fetch backwards.

Both `initialPageParam` and `getNextPageParam` are required, to
ensure the infinite query can properly fetch the next page of data.Also, `initialPageParam` may be specified when using the endpoint, to override the default value. `maxPages` and `getPreviousPageParam` are both optional.

[examples](docblock://query/endpointDefinitions.ts?token=InfiniteQueryExtraOptions.infiniteQueryOptions)

### `transformResponse`
Expand Down
171 changes: 171 additions & 0 deletions docs/rtk-query/usage/infinite-queries.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
id: infinite-queries
title: Infinite Queries
sidebar_label: Infinite Queries
hide_title: true
description: 'RTK Query > Usage > Infinite Queries: fetching many data pages from a server'
---

 

# Infinite Queries

## Overview

Rendering lists that can additively "load more" data onto an existing set of data or "infinite scroll" is also a very common UI pattern.

RTK Query supports this use case via "infinite query" endpoints. Infinite Query endpoints are similar to standard query endpoints, in that they fetch data and cache the results. However, infinite query endpoints have the ability to fetch "next" and "previous" pages, and contain all related fetched pages in a single cache entry.

## Infinite Query Concepts

RTK Query's support for infinite queries is modeled after [React Query's infinite query API design](https://tanstack.com/query/latest/docs/framework/react/guides/infinite-queries).

### Query Args, Page Params, and Cache Structure

With standard query endpoints:

- You specify the "query arg" value, which is passed to either the `query` function or the `queryFn` function that will calculate the desired URL or do the actual fetching
- The query arg is also serialized to generate the unique internal key for this specific cache entry
- The single response value is directly stored as the `data` field in the cache entry

Infinite queries work similarly, but have a couple additional layers:

- You still specify a "query arg", which is still used to generate the unique cache key for this specific cache entry
- However, there is a separation between the "query arg" used for the cache key, and the "page param" used to fetch a specific page. This means the page param is what will be passed to your `query` or `queryFn` methods.
- The `data` field in the cache entry stores a `{pages: Array<DataType>, pageParams: PageParam[]}` structure that contains _all_ of the fetched page results and their corresponding page params used to fetch them.

For example, a Pokemon API endpoint might have a string query arg like `"fire"`, but use a page number as the param to determine which page to fetch out of the results. The resulting cache data might look like this:

```ts no-transpile
{
queries: {
"getPokemon('fire')": {
data: {
pages: [
["Charmander", "Charmeleon"],
["Charizard", "Vulpix"],
["Magmar", "Flareon"]
],
pageParams: [
1,
2,
3
]
}
}
}
}
```

This structure allows flexibility in how your UI chooses to render the data (showing individual pages, flattening into a single list), enables limiting how many pages are kept in cache, and makes it possible to dynamically determine the next or previous page to fetch based on either the data or the page params.

## Defining Infinite Query Endpoints

Infinite query endpoints are defined by returning an object inside the `endpoints` section of `createApi`, and defining the fields using the `build.infiniteQuery()` method. They are an extension of standard query endpoints - you can specify [the same options as standard queries](./queries.mdx#defining-query-endpoints) (providing either `query` or `queryFn`, customizing with `transformResponse`, lifecycles with `onCacheEntryAdded` and `onQueryStarted`, etc). However, they also require an additional `infiniteQueryOptions` field to specify the infinite query behavior.

With TypeScript, you must supply 3 generic arguments: `build.infiniteQuery<ResultType, QueryArg, PageParam>`, where `ResultType` is the contents of a single page, `QueryArg` is the type passed in as the cache key, and `PageParam` is the value that will be passed to `query/queryFn` to make the rest. If there is no argument, use `void` for the arg type instead.

### `infiniteQueryOptions`

The `infiniteQueryOptions` field includes:

- `initialPageParam`: the default page param value used for the first request, if this was not specified at the usage site
- `maxPages`: an optional limit to how many fetched pages will be kept in the cache entry at a time
- `getNextPageParam`: a required callback you must provide to calculate the next page param, given the existing cached pages and page params
- `getPreviousPageParam`: an optional callback that will be used to calculate the previous page param, if you try to fetch backwards.

Both `initialPageParam` and `getNextPageParam` are required, to
ensure the infinite query can properly fetch the next page of data.Also, `initialPageParam` may be specified when using the endpoint, to override the default value. `maxPages` and `getPreviousPageParam` are both optional.

### Page Param Functions

`getNextPageParam` and `getPreviousPageParam` are user-defined, giving you flexibility to determine how those values are calculated:

```ts
export type PageParamFunction<DataType, PageParam> = (
currentPage: DataType,
allPages: Array<DataType>,
currentPageParam: PageParam,
allPageParams: Array<PageParam>,
) => PageParam | undefined | null
```
This enables a number of possible infinite query use cases, including cursor-based and limit+offset-based queries.
The "current" arguments will be either the last page for `getNext`, or the first page for `getPrevious`.
If there is no possible page to fetch in that direction, the callback should return `undefined`.
### Infinite Query Definition Example
A complete example of this might look like:
```ts no-transpile title="Infinite Query definition example"
type Pokemon = {
id: string
name: string
}

const pokemonApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (build) => ({
getInfinitePokemonWithMax: build.infiniteQuery<Pokemon[], string, number>({
infiniteQueryOptions: {
initialPageParam: 0,
maxPages: 3,
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) =>
lastPageParam + 1,
getPreviousPageParam: (
firstPage,
allPages,
firstPageParam,
allPageParams,
) => {
return firstPageParam > 0 ? firstPageParam - 1 : undefined
},
},
query(pageParam) {
return `https://example.com/listItems?page=${pageParam}`
},
}),
}),
})
```

## Performing Infinite Queries with React Hooks

[Similar to query endpoints](./queries.mdx#performing-queries-with-react-hooks), RTK Query will automatically generate React hooks for infinite query endpoints based on the name of the endpoint. An endpoint field with `getPokemon: build.infiniteQuery()` will generate a hook named `useGetPokemonInfiniteQuery`.

### Hook Types

There are 3 infinite query-related hooks:

1. [`useInfiniteQuery`]()

- Composes `useInfiniteQuerySubscription` and `useInfiniteQueryState`, and is the primary hook. Automatically triggers fetches of data from an endpoint, 'subscribes' the component to the cached data, and reads the request status and cached data from the Redux store.

2. [`useInfiniteQuerySubscription`]()

- Returns a `refetch` function and accepts all hooks options. Automatically triggers refetches of data from an endpoint, and 'subscribes' the component to the cached data.

3. [`useInfiniteQueryState`]()

- Returns the query state and accepts `skip` and `selectFromResult`. Reads the request status and cached data from the Redux store.

In practice, the standard `useInfiniteQuery`-based hooks such as `useGetPokemonInfiniteQuery` will be the primary hooks used in your application, but the other hooks are available for specific use cases.

### Query Hook Options

The query hooks expect two parameters: `(queryArg?, queryOptions?)`.

Unlike normal query hooks, your `query` or `queryFn` callbacks will receive a "page param" value to generate the URL or make the request. By default, the `initialPageParam` value specified in the endpoint will be used to make the first request, and then your `getNext/PreviousPageParam` callbacks will be used to calculate further page params as you fetch forwards or backwards.

If you want to start from a different page param, you may override the `initialPageParam` by passing it as part of the hook options:

```ts no-transpile
const { data } = useGetPokemonInfiniteQuery('fire', {
initialPageParam: 3,
})
```

The next and previous page params will still be calculated as needed.
1 change: 1 addition & 0 deletions website/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const sidebars: SidebarsConfig = {
collapsed: true,
items: [
'rtk-query/usage/queries',
'rtk-query/usage/infinite-queries',
'rtk-query/usage/mutations',
'rtk-query/usage/cache-behavior',
'rtk-query/usage/automated-refetching',
Expand Down

0 comments on commit 40568c1

Please sign in to comment.