useInfiniteList
useInfiniteList
is an extended version of TanStack Query's useInfiniteQuery
used for retrieving items from a resource
with pagination, sort, and filter configurations. It is ideal for lists where the total number of records is unknown and the user loads the next pages with a button.
It uses the
getList
method as the query function from thedataProvider
which is passed to<Refine>
.It uses a query key to cache the data. The query key is generated from the provided properties. You can see the query key by using the TanStack Query devtools.
Basic Usage
Here is a basic example of how to use the useInfiniteList
hook.
Pagination
useInfiniteList
hook supports pagination properties just like useList
. To handle pagination, the useInfiniteList
hook passes the pagination
property to the getList
method from the dataProvider
.
Dynamically changing the pagination
properties will trigger a new request. Also, the fetchNextPage
method will increase the pagination.current
property by one and trigger a new request.
import { useInfiniteList } from "@pankod/refine-core";
const postListQueryResult = useInfiniteList({
resource: "posts",
config: {
pagination: { current: 3, pageSize: 8 },
},
});
Sorting
The useInfiniteList
hook supports the sorting feature. You can pass the sort
property to enable sorting. To handle sorting, the useInfiniteList
hook passes the sort
property to the getList
method from the dataProvider
.
Dynamically changing the sort
property will trigger a new request.
Filtering
The useInfiniteList
hook supports the filtering feature. You can pass the filters
property to enable filtering. To handle filtering, the useInfiniteList
hook passes the filters
property to the getList
method from the dataProvider
.
Dynamically changing the filters
property will trigger a new request.
Realtime Updates
This feature is only available if you use a Live Provider.
When the useInfiniteList
hook is mounted, it will call the subscribe
method from the liveProvider
with some parameters such as channel
, resource
etc. It is useful when you want to subscribe to live updates.
Refer to the liveProvider
documentation for more information →
Properties
resource
required
It will be passed to the getList
method from the dataProvider
as a parameter. The parameter is usually used as an API endpoint path. It all depends on how to handle the resource
in the getList
method. See the creating a data provider section for an example of how resources are handled.
useInfiniteList({
resource: "categories",
});
dataProviderName
If there is more than one dataProvider
, you can specify which one to use by passing the dataProviderName
prop. It is useful when you have a different data provider for different resources.
useInfiniteList({
dataProviderName: "second-data-provider",
});
config.filters
filters
will be passed to the getList
method from the dataProvider
as a parameter. It is used to send filter query parameters to the API.
Refer to the CrudFilters
interface for more information →
useInfiniteList({
config: {
filters: [
{
field: "title",
operator: "contains",
value: "Foo",
},
],
},
});
config.sort
sort
will be passed to the getList
method from the dataProvider
as a parameter. It is used to send sort query parameters to the API.
Refer to the CrudSorting
interface for more information →
useInfiniteList({
config: {
sort: [
{
field: "title",
order: "asc",
},
],
},
});
config.pagination
pagination
will be passed to the getList
method from the dataProvider
as a parameter. It is used to send pagination query parameters to the API.
current
You can pass the current
page number to the pagination
property.
useInfiniteList({
config: {
pagination: {
current: 2,
},
},
});
pageSize
You can pass the pageSize
to the pagination
property.
useInfiniteList({
config: {
pagination: {
pageSize: 20,
},
},
});
config.hasPagination
hasPagination
will be passed to the getList
method from the dataProvider
as a parameter. It is used to determine whether to use server-side pagination or not.
useInfiniteList({
config: {
hasPagination: false,
},
});
queryOptions
queryOptions
is used to pass additional options to the useQuery
hook. It is useful when you want to pass additional options to the useQuery
hook.
Refer to the useQuery
documentation for more information →
useInfiniteList({
queryOptions: {
retry: 3,
},
});
metaData
metaData
is used following two purposes:
- To pass additional information to data provider methods.
- Generate GraphQL queries using plain JavaScript Objects (JSON). Please refer GraphQL for more information.
In the following example, we pass the headers
property in the metaData
object to the create
method. With similar logic, you can pass any properties to specifically handle the data provider methods.
useInfiniteList({
metaData: {
headers: { "x-meta-data": "true" },
},
});
const myDataProvider = {
//...
getList: async ({
resource,
pagination,
hasPagination,
sort,
filters,
metaData,
}) => {
const headers = metaData?.headers ?? {};
const url = `${apiUrl}/${resource}`;
//...
//...
const { data } = await httpClient.get(`${url}`, { headers });
return {
data,
};
},
//...
};
successNotification
NotificationProvider
is required for this prop to work.
After data is fetched successfully, useInfiniteList
can call open
function from NotificationProvider
to show a success notification. With this prop, you can customize the success notification.
useInfiniteList({
successNotification: (data, values, resource) => {
return {
message: `${data.title} Successfully fetched.`,
description: "Success with no errors",
type: "success",
};
},
});
errorNotification
NotificationProvider
is required for this prop to work.
After data fetching is failed, useInfiniteList
will call open
function from NotificationProvider
to show an error notification. With this prop, you can customize the error notification.
useInfiniteList({
errorNotification: (data, values, resource) => {
return {
message: `Something went wrong when getting ${data.id}`,
description: "Error",
type: "error",
};
},
});
liveMode
LiveProvider
is required for this prop to work.
Determines whether to update data automatically ("auto") or not ("manual") if a related live event is received. It can be used to update and show data in Realtime throughout your app. For more information about live mode, please check the Live / Realtime page.
useInfiniteList({
liveMode: "auto",
});
onLiveEvent
LiveProvider
is required for this prop to work.
The callback function is executed when new events from a subscription have arrived.
useInfiniteList({
onLiveEvent: (event) => {
console.log(event);
},
});
liveParams
LiveProvider
is required for this prop to work.
Params to pass to liveProvider's subscribe method.
Return Values
Returns an object with TanStack Query's useInfiniteQuery
return values.
Refer to the useInfiniteQuery
documentation for more information →
FAQ
How to use cursor-based pagination?
Some APIs use the cursor-pagination
method for its benefits. This method uses a cursor
object to determine the next set of data. The cursor can be a number or a string and is passed to the API as a query parameter.
Preparing the data provider:
Consumes data from data provider useInfiniteList
with the getList
method. First of all, we need to make this method in the data provider convenient for this API. The cursor
data is kept in pagination
and should be set to 0
by default.
getList: async ({ resource, pagination }) => {
const { current } = pagination;
const { data } = await axios.get(
`https://api.fake-rest.refine.dev/${resource}?cursor=${current || 0}`,
);
return {
data: data[resource],
total: 0,
};
},
As the total
data is only needed in the offset-pagination
method, define it as 0
here.
After this process, we successfully retrieved the first page of data. Let's fill the cursor
object for the next page.
getList: async ({ resource, pagination }) => {
const { current } = pagination;
const { data } = await axios.get(
`https://api.fake-rest.refine.dev/${resource}?cursor=${current || 0}`,
);
return {
data: data[resource],
total: 0,
cursor: {
next: data.cursor.next,
prev: data.cursor.prev,
},
};
},
How to override the getNextPageParam
method?
By default, refine
expects you to return the cursor
object, but is not required. This is because some APIs don't work that way. To fix this problem you need to override the getNextPageParam
method and return the next cursor
.
import { useInfiniteList } from "@pankod/refine-core";
const {
data,
error,
hasNextPage,
isLoading,
fetchNextPage,
isFetchingNextPage,
} = useInfiniteList({
resource: "posts",
queryOptions: {
getNextPageParam: (lastPage, allPages) => {
// return the last post's id
const { data } = lastPage;
const lastPost = data[data.length - 1];
return lastPost.id;
},
},
});
When you override this method, you can access the lastPage
and allPages
.
API
Properties
Config Parameters
interface UseInfiniteListConfig {
hasPagination?: boolean;
pagination?: {
current?: number;
pageSize?: number;
};
sort?: Array<{
field: string;
order: "asc" | "desc";
}>;
filters?: Array<{
field: string;
operator: CrudOperators;
value: any;
}>;
}
Type Parameters
Property | Desription | Type | Default |
---|---|---|---|
TData | Result data of the query. Extends BaseRecord | BaseRecord | BaseRecord |
TError | Custom error object that extends HttpError | HttpError | HttpError |
Return Values
Description | Type |
---|---|
Result of the TanStack Query's useInfiniteQuery | InfiniteQueryObserverResult<{ data: TData[]; total: number; }, TError> |
Example
npm create refine-app@latest -- --example use-infinite-list