自定义分页 - React table

问题描述 投票:0回答:2

我想使用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>
  );
};

谁能帮我定制我的分页?我一直在阅读一些文档,但我正在努力实施解决方案

reactjs typescript react-table
2个回答
2
投票

首先,创建一个名为 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版本:

Edit quiet-browser-z5duq2

打字稿版本:

Edit awesome-murdock-2heycu


0
投票
使用

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

请注意,为简洁起见,省略了可访问性和其他详细信息。

你可以看一下这个直接从官方例子修改而来的例子。

Edit tanstack-table-pagination-range

© www.soinside.com 2019 - 2024. All rights reserved.