Skip to main content
Version: 3.xx.xx

useInfiniteList

useInfiniteList is a modified version of react-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 the dataProvider which is passed to <Refine>.

http://localhost:3000/categories
import React from "react";
import { useInfiniteList } from "@pankod/refine-core";

const PostList = () => {
const {
data,
error,
hasNextPage,
isLoading,
fetchNextPage,
isFetchingNextPage,
} = useInfiniteList({
resource: "categories",
config: {
pagination: {
pageSize: 4
}
}
});

if (isLoading) {
return <p>Loading</p>;
}
if (error) {
return <p>Something went wrong</p>;
}

return (
<div>
<ul>
{data?.pages.map((page) =>
page.data.map(({ id, title }) => (
<li key={id}>
{id}.{title}
</li>
)),
)}
</ul>

{
hasNextPage && (
<button
onClick={() => fetchNextPage()}
disabled={isFetchingNextPage}
>
{isFetchingNextPage ? "Loading more..." : "Load More" }
</button>
)
}
</div>
);
}

Usage

Let's assume that we have a posts resource with the following data:

https://api.fake-rest.refine.dev/posts
{
[
{
id: 1,
title: "E-business",
status: "draft",
},
{
id: 2,
title: "Virtual Invoice Avon",
status: "published",
},
{
id: 3,
title: "Powerful Crypto",
status: "rejected",
},
];
}

First of all, we will use useInfiniteList without passing any query configurations.

import { useInfiniteList } from "@pankod/refine-core";

type IPost = {
id: number;
title: string;
status: "rejected" | "published" | "draft";
};

const postInfiniteListResult = useInfiniteList<IPost>({ resource: "posts" });
postInfiniteListResult
{
"status": "success",
"data": {
"pages": [
{
"data": [
{
"id": 1,
"title": "E-business",
"status": "draft"
},
{
"id": 2,
"title": "Virtual Invoice Avon",
"status": "published"
}
],
"total": 1370
}
]
},
"hasNextPage": true,
"hasPreviousPage": false,
"isFetchingNextPage": false,
"isFetchingPreviousPage": false
...
}

If your API returns the result like above, you can use useInfiniteList without any configuration.

caution

getList also has default values for pagination:

{
pagination: { current: 1, pageSize: 10 }
}
caution

If you want to create your own getList method, it will automatically implement default query configurations since useInfiniteList can work with no configuration parameters.

Query Configuration

pagination

Allows us to set page and items per page values.

For example imagine that we have 1000 post records:

import { useInfiniteList } from "@pankod/refine-core";

const postListQueryResult = useInfiniteList({
resource: "posts",
config: {
pagination: { current: 3, pageSize: 8 },
},
});

sort

Allows us to sort records by the speficified order and field.

import { useInfiniteList } from "@pankod/refine-core";

const postListQueryResult = useInfiniteList<IPost>({
resource: "posts",
config: {
sort: [{ order: "asc", field: "title" }],
},
});

filters

Allows us to filter queries using refine's filter operators. It is configured via field, operator and value properites.

Refer to supported operators.

import { useInfiniteList } from "@pankod/refine-core";

const postListQueryResult = useInfiniteList<IPost>({
resource: "posts",
config: {
filters: [
{
field: "status",
operator: "eq",
value: "rejected",
},
],
},
});
tip

useInfiniteList returns the result of react-query's useInfiniteQuery which includes many properties such as fetchNextPage, hasNextPage and isFetchingNextPage.

  • For example, to disable query from running automatically you can set enabled to false.
import { useInfiniteList } from "@pankod/refine-core";

const postListQueryResult = useInfiniteList<IPost>({
resource: "posts",
queryOptions: {
enabled: false,
getNextPageParam: ({ data }) => {
const lastRow = data[data.length - 1];
return lastRow.id;
},
},
});

Refer to react-query docs for further information.

API

Properties

Config parameters

interface UseListConfig {
hasPagination?: boolean;
pagination?: {
current?: number;
pageSize?: number;
};
sort?: Array<{
field: string;
order: "asc" | "desc";
}>;
filters?: Array<{
field: string;
operator: CrudOperators;
value: any;
}>;
}

Type Parameters

PropertyDesriptionTypeDefault
TDataResult data of the query. Extends BaseRecordBaseRecordBaseRecord
TErrorCustom error object that extends HttpErrorHttpErrorHttpError

Return values

DescriptionType
Result of the react-query's useInfiniteQueryInfiniteQueryObserverResult<{
data: TData[];
total: number; },
TError>

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 getList method. First of all, we need to make the 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,
};
},
tip

As the total data is only needed in the offset-pagination method, define it as 0 here.

After this process, we have successfully retrieved the first page 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;
},
},
});
tip

When you override this method, you can access the lastPage and allPages.