I want to make a custom pagination using react-table. It needs to look quite a specific way:
Currently it looks like this, I guess this is the default for the current pagination I am using:
I need it to look like this:
My code is the following:
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>
);
};
Can anyone help me to customize my pagination?? I have been reading some docs but I am struggling to implement a solution
First, create a new file called 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
};
};
Next, create Pagination.js by implementing our usePaginationPages hook
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);
You can see the full code here:
Javascript version:
Typescript version: