or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced.mdcore-request.mdindex.mdload-more.mdpagination.md
tile.json

pagination.mddocs/

Pagination

Advanced pagination support with table integration, sorting, filtering capabilities, and Ant Design compatibility. Provides comprehensive pagination state management and table props for seamless integration.

Capabilities

Paginated useRequest

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>;

usePaginated

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
  }
);

Data Types

Pagination Parameters

/**
 * 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 Integration Types

/**
 * 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;
}

Configuration Options

Basic Pagination Options

interface BasePaginatedOptions<U>
  extends Omit<BaseOptions<PaginatedFormatReturn<U>, PaginatedParams>, 'paginated'> {
  /** Enable pagination mode */
  paginated: true;
  /** Default number of items per page */
  defaultPageSize?: number;
}

Pagination with Formatting

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
});

Result Interface

PaginatedResult

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}
/>;

Advanced Usage

Sorting and Filtering

// 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
    }
  ]}
/>;

Custom Pagination Controls

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>
);

Server-Side Pagination Best Practices

// 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
  })
});

Pagination with Search

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
};

Error Handling in Pagination

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>
  );
}