我想使用react-table 进行自定义分页。它需要看起来非常具体:
目前看起来像这样,我想这是我正在使用的当前分页的默认值:
我需要它看起来像这样:
我的代码如下:
import React, { useEffect, useState } from 'react';
import { useTable, usePagination } from 'react-table';
import { TableProps } from '../../../types';
export const Table: React.FC<TableProps> = ({
columns,
data,
isLoading,
hasPagination = true,
pageNumber,
onPageChange,
maxPage,
onRowClick = () => undefined,
maxCount,
}) => {
const [canPreviousPage, setCanPreviousPage] = useState(false);
const [canNextPage, setCanNextPage] = useState(false);
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
pageCount,
nextPage,
previousPage,
} = useTable(
{
columns,
data,
manualPagination: true,
pageCount: maxPage,
},
usePagination,
);
useEffect(() => {
if (pageNumber <= 1) {
setCanPreviousPage(false);
} else {
setCanPreviousPage(true);
}
if (maxPage > pageNumber) {
setCanNextPage(true);
} else {
setCanNextPage(false);
}
}, [pageNumber, maxPage]);
const onNextPage = () => {
nextPage();
onPageChange(pageNumber + 1);
};
const onPreviousPage = () => {
previousPage();
onPageChange(pageNumber - 1);
};
const Pagination = () => {
if (!hasPagination) {
return null;
}
return (
<div className="pagination flex items-center text-black mt-3">
<Button onClick={onPreviousPage} disabled={!canPreviousPage}>
<Icon className={canPreviousPage ? 'color-black' : 'color-grey-200'}>chevron_left</Icon>
</Button>
<span>
Page{' '}
<strong>
{pageNumber} of {pageCount}
</strong>
</span>
<Button onClick={onNextPage} disabled={!canNextPage}>
<Icon className={canNextPage ? 'color-black' : 'color-grey-200'}>chevron_right</Icon>
</Button>
<div>{maxCount} records found</div>
<div className="mx-1">
<ActivityLoader isLoading={isLoading} />
</div>
</div>
);
};
return (
<div className="pt-10 pl-20 pr-16 font-nunito font-medium text-base">
<div className="align-middle inline-block w-full w-40 text-left">
<div className="shadow border-b border-gray-200">
<table {...getTableProps} className="w-full divide-y divide-gray-200">
<thead className="bg-mainBlue p-16">
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th
scope="col"
className="px-6 py-4 text-left font-medium text-gray-500 p-16 bg-mainBlue text-base"
{...column.getHeaderProps()}
>
{column.render('Header')}
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()} className="bg-white divide-y divide-gray-200">
{page.map((row) => {
prepareRow(row);
return (
<tr {...row.getRowProps()} onClick={() => onRowClick(_.get(row, 'original.id'))}>
{row.cells.map((cell) => {
return (
<td
className="px-6 py-4 whitespace-nowrap text-black"
{...cell.getCellProps()}
>
{cell.render('Cell')}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
</div>
<Pagination />
</div>
);
};
谁能帮我定制我的分页?我一直在阅读一些文档,但我正在努力实施解决方案
首先,创建一个名为 usePaginationPages.js
的新文件import { useCallback, useEffect, useMemo, useState } from "react";
export const usePaginationPages = ({ gotoPage, length, pageSize }) => {
const [currentPage, setCurrentPage] = useState(1);
const totalPages = useMemo(() => {
return Math.ceil(length / pageSize);
}, [length, pageSize]);
const canGo = useMemo(() => {
return {
next: currentPage < totalPages,
previous: currentPage - 1 > 0
};
}, [currentPage, totalPages]);
// currentPage siblings
const pages = useMemo(() => {
const start = Math.floor((currentPage - 1) / 5) * 5;
const end = start + 5 > totalPages ? totalPages : start + 5;
return Array.from({ length: end - start }, (_, i) => start + i + 1);
}, [currentPage, totalPages]);
// programatically call gotoPage when currentPage changes
useEffect(() => {
gotoPage(currentPage - 1);
}, [currentPage, gotoPage]);
// show first page when per page select options changes
useEffect(() => {
if (pageSize) {
goTo(1);
}
}, [pageSize]);
const goTo = (pg) => {
setCurrentPage(pg);
};
const goNext = useCallback(() => {
if (canGo.next) {
setCurrentPage((prev) => prev + 1);
}
}, [canGo]);
const goPrev = useCallback(() => {
if (canGo.previous) {
setCurrentPage((prev) => prev - 1);
}
}, [canGo]);
return {
canGo,
currentPage,
pages,
goTo,
goNext,
goPrev
};
};
接下来,通过实现我们的 usePaginationPages 钩子来创建 Pagination.js
import { useState, useEffect, memo } from "react";
import { ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/24/solid";
import { usePaginationPages } from "./usePaginationPages";
function Pagination({ gotoPage, length, pageSize, setPageSize }) {
const [perPage, setPerPage] = useState(pageSize);
const {
canGo,
currentPage,
pages,
goTo,
goNext,
goPrev
} = usePaginationPages({
gotoPage,
length,
pageSize
});
// update pageSize when perPage changes
useEffect(() => {
// don't forget set to Number
setPageSize(Number(perPage));
}, [perPage, setPageSize]);
return (
<div className="m-4 flex items-center justify-center">
<button
onClick={goPrev}
disabled={!canGo.previous}
className="m-1 px-2 py-1 border rounded-md"
>
<ChevronLeftIcon className="h-6 w-4 text-blue-500" />
</button>
{pages.map((page, i) => (
<button
onClick={() => goTo(page)}
key={i}
style={{
background: currentPage === page ? "blue" : "none",
color: currentPage === page ? "white" : "black"
}}
className="m-1 px-3 py-1 border rounded-md"
>
{page}
</button>
))}
<button
onClick={goNext}
disabled={!canGo.next}
className="m-1 px-2 py-1 border rounded-md"
>
<ChevronRightIcon className="h-6 w-4 text-blue-500" />
</button>
<select
className="px-2 py-[6px] border rounded-md w-30 bg-white"
value={pageSize}
onChange={(e) => setPerPage(e.target.value)}
>
{[10, 50, 100].map((pageSize) => (
<option className="py-2" value={pageSize} key={pageSize}>
{pageSize} / page
</option>
))}
</select>
</div>
);
}
export default memo(Pagination);
您可以在此处查看完整代码:Javascript版本:
打字稿版本:
TanStack Table进行分页时,通常应该遵循官方指南,利用pagination
状态和
onPaginationChange
。建议在 React 表中使用
pagination
状态作为事实来源 — 许多逻辑已由库处理,您不需要单独的状态
canPreviousPage
,而应直接使用
table.getCanPreviousPage()
。关于分页范围,可以使用这个基于Mantine的
usePagination编写的hook。该钩子直接从 pagination
实例读取现有
table
状态并计算分页范围。
use-react-table-pagination-range.ts
import type { Table } from '@tanstack/react-table';
const range = (start: number, end: number) => {
const length = end - start + 1;
return Array.from({ length }, (_, index) => index + start);
};
export const ELLIPSIS = 'ellipsis';
export const useReactTablePaginationRange = <TData>(
table: Table<TData>,
/**
* Siblings amount on left/right side of selected page, defaults to 1.
*/
siblings = 1,
/**
* Amount of elements visible on left/right edges, defaults to 1.
*/
boundaries = 1
) => {
const total = table.getPageCount();
const activePage = table.getState().pagination.pageIndex + 1;
const totalPageNumbers = siblings * 2 + 3 + boundaries * 2;
if (totalPageNumbers >= total) {
return range(1, total);
}
const leftSiblingIndex = Math.max(activePage - siblings, boundaries);
const rightSiblingIndex = Math.min(activePage + siblings, total - boundaries);
const shouldShowLeftDots = leftSiblingIndex > boundaries + 2;
const shouldShowRightDots = rightSiblingIndex < total - (boundaries + 1);
if (!shouldShowLeftDots && shouldShowRightDots) {
const leftItemCount = siblings * 2 + boundaries + 2;
return [
...range(1, leftItemCount),
ELLIPSIS,
...range(total - (boundaries - 1), total),
] as const;
}
if (shouldShowLeftDots && !shouldShowRightDots) {
const rightItemCount = boundaries + 1 + 2 * siblings;
return [
...range(1, boundaries),
ELLIPSIS,
...range(total - rightItemCount, total),
] as const;
}
return [
...range(1, boundaries),
ELLIPSIS,
...range(leftSiblingIndex, rightSiblingIndex),
ELLIPSIS,
...range(total - boundaries + 1, total),
] as const;
};
使用方法import { useReactTable } from "@tanstack/react-table"
import { ELLIPSIS, useReactTablePaginationRange } from "@/components/hooks/use-react-table-pagination-range";
const Example = () => {
const table = useReactTable({ ... });
const paginationRange = useReactTablePaginationRange(table);
return (
<>
...
{paginationRange.map((page, index) => (
<div key={index}>
{page === ELLIPSIS ? (
<span>…</span>
) : (
<button
onClick={() => table.setPageIndex(page - 1)}
>
{page}
</button>
)}
</div>
))}
...
</>
)
}
请注意,为简洁起见,省略了可访问性和其他详细信息。你可以看一下这个直接从官方例子修改而来的例子。