Advanced pagination support with table integration, sorting, filtering capabilities, and Ant Design compatibility. Provides comprehensive pagination state management and table props for seamless integration.
Specialized version of useRequest for paginated data with automatic pagination parameter management and table integration support.
/**
* useRequest with pagination support
* @param service - Service function expecting pagination parameters
* @param options - Pagination configuration options
* @returns Paginated result with pagination controls and table props
*/
function useRequest<R = any, Item = any, U extends Item = any>(
service: CombineService<R, PaginatedParams>,
options: PaginatedOptionsWithFormat<R, Item, U>
): PaginatedResult<Item>;
/**
* useRequest with pagination and expected data format
* @param service - Service function returning PaginatedFormatReturn
* @param options - Basic pagination options
* @returns Paginated result with pagination controls
*/
function useRequest<R = any, Item = any, U extends Item = any>(
service: CombineService<PaginatedFormatReturn<Item>, PaginatedParams>,
options: BasePaginatedOptions<U>
): PaginatedResult<Item>;Direct pagination hook for when you need explicit pagination functionality.
/**
* Direct pagination hook with response formatting
* @param service - Service function with pagination parameters
* @param options - Pagination options with formatResult
* @returns Paginated result with controls
*/
function usePaginated<R, Item, U extends Item = any>(
service: (...p: PaginatedParams) => Promise<R>,
options: PaginatedOptionsWithFormat<R, Item, U>
): PaginatedResult<Item>;
/**
* Direct pagination hook with expected format
* @param service - Service function returning expected pagination format
* @param options - Basic pagination options
* @returns Paginated result with controls
*/
function usePaginated<R, Item, U extends Item = any>(
service: (...p: PaginatedParams) => Promise<PaginatedFormatReturn<Item>>,
options: BasePaginatedOptions<U>
): PaginatedResult<Item>;Usage Examples:
import useRequest, { usePaginated } from "@ahooksjs/use-request";
// Basic pagination with useRequest
const {
data,
loading,
pagination,
tableProps
} = useRequest(
({ current, pageSize }) => `/api/users?page=${current}&size=${pageSize}`,
{
paginated: true,
defaultPageSize: 10
}
);
// With response formatting
const paginatedUsers = useRequest(
({ current, pageSize }) => `/api/users?page=${current}&size=${pageSize}`,
{
paginated: true,
defaultPageSize: 20,
formatResult: (response) => ({
total: response.totalCount,
list: response.users.map(user => ({
id: user.id,
name: user.fullName,
email: user.emailAddress
}))
})
}
);
// Direct usePaginated
const usersPagination = usePaginated(
async ({ current, pageSize, sorter, filters }) => {
const params = new URLSearchParams({
page: current.toString(),
size: pageSize.toString()
});
if (sorter?.field) {
params.append('sortBy', sorter.field);
params.append('sortOrder', sorter.order);
}
if (filters) {
Object.entries(filters).forEach(([key, value]) => {
if (value) params.append(key, value.toString());
});
}
const response = await fetch(`/api/users?${params}`);
return response.json();
},
{
paginated: true,
defaultPageSize: 15
}
);/**
* Parameters passed to paginated service functions
*/
type PaginatedParams = [
{
/** Current page number (1-based) */
current: number;
/** Number of items per page */
pageSize: number;
/** Sort configuration */
sorter?: Sorter;
/** Filter configuration */
filters?: Filter;
},
/** Additional custom parameters */
...any[]
];
/**
* Expected response format for pagination
*/
interface PaginatedFormatReturn<Item> {
/** Total number of items */
total: number;
/** Current page items */
list: Item[];
/** Additional response data */
[key: string]: any;
}/**
* Ant Design table sorter configuration
*/
interface Sorter {
field?: string;
order?: 'ascend' | 'descend';
[key: string]: any;
}
/**
* Ant Design table filter configuration
*/
interface Filter {
[key: string]: any;
}
/**
* Ant Design pagination configuration
*/
interface PaginationConfig {
current?: number;
pageSize?: number;
total?: number;
showSizeChanger?: boolean;
showQuickJumper?: boolean;
showTotal?: (total: number, range: [number, number]) => string;
[key: string]: any;
}interface BasePaginatedOptions<U>
extends Omit<BaseOptions<PaginatedFormatReturn<U>, PaginatedParams>, 'paginated'> {
/** Enable pagination mode */
paginated: true;
/** Default number of items per page */
defaultPageSize?: number;
}interface PaginatedOptionsWithFormat<R, Item, U>
extends Omit<BaseOptions<PaginatedFormatReturn<U>, PaginatedParams>, 'paginated'> {
/** Enable pagination mode */
paginated: true;
/** Default number of items per page */
defaultPageSize?: number;
/** Format response to expected pagination structure */
formatResult: (data: R) => PaginatedFormatReturn<Item>;
}Usage Examples:
// Basic options
const users = useRequest('/api/users', {
paginated: true,
defaultPageSize: 20,
manual: false, // Auto-load first page
onSuccess: (data) => {
console.log(`Loaded ${data.list.length} users`);
}
});
// With formatting
const products = useRequest('/api/products', {
paginated: true,
defaultPageSize: 12,
formatResult: (response) => ({
total: response.pagination.totalItems,
list: response.data.map(product => ({
id: product.productId,
name: product.productName,
price: product.priceAmount,
category: product.categoryInfo.name
}))
}),
cacheKey: 'products-list',
refreshOnWindowFocus: true
});Complete pagination result with data, controls, and table integration props.
interface PaginatedResult<Item>
extends BaseResult<PaginatedFormatReturn<Item>, PaginatedParams> {
/** Pagination controls and state */
pagination: {
/** Current page number */
current: number;
/** Items per page */
pageSize: number;
/** Total number of items */
total: number;
/** Total number of pages */
totalPage: number;
/** Change page and page size */
onChange: (current: number, pageSize: number) => void;
/** Change current page only */
changeCurrent: (current: number) => void;
/** Change page size only */
changePageSize: (pageSize: number) => void;
/** Additional pagination properties */
[key: string]: any;
};
/** Ant Design table props for direct integration */
tableProps: {
/** Current page data */
dataSource: Item[];
/** Loading state */
loading: boolean;
/** Table change handler (pagination, filters, sorter) */
onChange: (pagination: PaginationConfig, filters?: Filter, sorter?: Sorter) => void;
/** Pagination configuration for table */
pagination: PaginationConfig;
/** Additional table properties */
[key: string]: any;
};
/** Current sort configuration */
sorter?: Sorter;
/** Current filter configuration */
filters?: Filter;
}Usage Examples:
const {
data, // { total: number, list: Item[] }
loading, // Loading state
pagination, // Pagination controls
tableProps, // Ant Design table props
sorter, // Current sort
filters, // Current filters
run, // Manual execution
refresh // Refresh current page
} = useRequest('/api/users', {
paginated: true,
defaultPageSize: 10
});
// Manual pagination control
const handlePageChange = (page: number) => {
pagination.changeCurrent(page);
};
const handlePageSizeChange = (size: number) => {
pagination.changePageSize(size);
};
// Direct table integration (Ant Design)
<Table {...tableProps} />;
// Custom table integration
<CustomTable
data={data?.list || []}
loading={loading}
currentPage={pagination.current}
pageSize={pagination.pageSize}
total={pagination.total}
onPageChange={pagination.onChange}
/>;// API with sorting and filtering support
const users = useRequest(
async ({ current, pageSize, sorter, filters }) => {
const params = new URLSearchParams({
page: current.toString(),
limit: pageSize.toString()
});
// Add sorting
if (sorter?.field) {
params.append('sortBy', sorter.field);
params.append('sortOrder', sorter.order === 'ascend' ? 'asc' : 'desc');
}
// Add filters
if (filters?.status) {
params.append('status', filters.status);
}
if (filters?.role) {
params.append('role', filters.role);
}
const response = await fetch(`/api/users?${params}`);
return response.json();
},
{
paginated: true,
defaultPageSize: 20
}
);
// Table with sorting and filtering
<Table
{...users.tableProps}
columns={[
{
title: 'Name',
dataIndex: 'name',
sorter: true, // Enable sorting
sortOrder: users.sorter?.field === 'name' ? users.sorter.order : null
},
{
title: 'Status',
dataIndex: 'status',
filters: [
{ text: 'Active', value: 'active' },
{ text: 'Inactive', value: 'inactive' }
],
filteredValue: users.filters?.status || null
}
]}
/>;const { pagination, loading } = useRequest('/api/products', {
paginated: true,
defaultPageSize: 12
});
const CustomPagination = () => (
<div className="pagination-controls">
<button
disabled={pagination.current === 1 || loading}
onClick={() => pagination.changeCurrent(pagination.current - 1)}
>
Previous
</button>
<span>
Page {pagination.current} of {pagination.totalPage}
</span>
<button
disabled={pagination.current === pagination.totalPage || loading}
onClick={() => pagination.changeCurrent(pagination.current + 1)}
>
Next
</button>
<select
value={pagination.pageSize}
onChange={(e) => pagination.changePageSize(Number(e.target.value))}
>
<option value={10}>10 per page</option>
<option value={20}>20 per page</option>
<option value={50}>50 per page</option>
</select>
</div>
);// Backend-agnostic pagination service
const createPaginatedService = (baseUrl: string) =>
async ({ current, pageSize, sorter, filters }: PaginatedParams[0]) => {
const searchParams = new URLSearchParams({
page: current.toString(),
per_page: pageSize.toString()
});
// Handle different sorting formats
if (sorter?.field) {
searchParams.append('sort', sorter.field);
searchParams.append('order', sorter.order === 'ascend' ? 'asc' : 'desc');
}
// Handle filters
Object.entries(filters || {}).forEach(([key, value]) => {
if (value !== null && value !== undefined) {
searchParams.append(`filter[${key}]`, String(value));
}
});
const response = await fetch(`${baseUrl}?${searchParams}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
};
// Usage with different APIs
const users = useRequest(createPaginatedService('/api/users'), {
paginated: true,
defaultPageSize: 25,
formatResult: (response) => ({
total: response.meta.total,
list: response.data
})
});
const orders = useRequest(createPaginatedService('/api/orders'), {
paginated: true,
defaultPageSize: 15,
formatResult: (response) => ({
total: response.pagination.count,
list: response.results
})
});const [searchTerm, setSearchTerm] = useState('');
const searchResults = useRequest(
({ current, pageSize }) => `/api/search?q=${searchTerm}&page=${current}&size=${pageSize}`,
{
paginated: true,
defaultPageSize: 20,
refreshDeps: [searchTerm], // Refresh when search changes
ready: searchTerm.length > 0, // Only search when term exists
formatResult: (response) => ({
total: response.totalHits,
list: response.results
})
}
);
// Search resets to page 1
const handleSearch = (term: string) => {
setSearchTerm(term);
// Pagination will automatically reset to page 1 due to refreshDeps
};const paginatedData = useRequest('/api/data', {
paginated: true,
defaultPageSize: 10,
onError: (error, [paginationParams]) => {
console.error('Pagination request failed:', {
error: error.message,
page: paginationParams.current,
pageSize: paginationParams.pageSize
});
// Optionally fallback to previous page on error
if (paginationParams.current > 1) {
// Could implement fallback logic here
}
},
throwOnError: false // Handle errors gracefully
});
// Display error state in UI
if (paginatedData.error) {
return (
<div className="error-state">
<p>Failed to load page {paginatedData.pagination.current}</p>
<button onClick={() => paginatedData.refresh()}>
Try Again
</button>
</div>
);
}