Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save philly-vanilly/a95f72684a01439160f67d5498bc3386 to your computer and use it in GitHub Desktop.
Save philly-vanilly/a95f72684a01439160f67d5498bc3386 to your computer and use it in GitHub Desktop.
react-table manual sort and pagination
import React, { useState, useEffect, useCallback } from "react";
import { useTable, useSortBy, usePagination } from "react-table";
import makeData from "./makeData";
const serverData = makeData(10000);
function Table({
columns,
data,
fetchData,
loading,
pageCount: controlledPageCount
}) {
const {
getTableProps,
getTableBodyProps,
headerGroups,
prepareRow,
page,
canPreviousPage,
canNextPage,
pageOptions,
gotoPage,
nextPage,
previousPage,
setPageSize,
state: { pageIndex, pageSize, sortBy }
} = useTable(
{
columns,
data,
manualPagination: true,
manualSortBy: true,
autoResetPage: false,
autoResetSortBy: false,
pageCount: controlledPageCount
},
useSortBy,
usePagination
);
useEffect(() => {
fetchData({ sortBy, pageIndex, pageSize });
}, [sortBy, fetchData, pageIndex, pageSize]);
return (
<>
<table {...getTableProps()}>
<thead>
{headerGroups.map((headerGroup) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
// Add the sorting props to control sorting. For this example
// we can add them into the header props
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
{column.render("Header")}
{/* Add a sort direction indicator */}
<span>
{column.isSorted
? column.isSortedDesc
? " 🔽"
: " 🔼"
: ""}
</span>
</th>
))}
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{page.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()}>{cell.render("Cell")}</td>
);
})}
</tr>
);
})}
<tr>
{loading ? (
// Use our custom loading state to show a loading indicator
<td colSpan="10000">Loading...</td>
) : (
<td colSpan="10000">
Showing {page.length} of ~{controlledPageCount * pageSize}{" "}
results
</td>
)}
</tr>
</tbody>
</table>
<div className="pagination">
<button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
{"<<"}
</button>{" "}
<button onClick={() => previousPage()} disabled={!canPreviousPage}>
{"<"}
</button>{" "}
<button onClick={() => nextPage()} disabled={!canNextPage}>
{">"}
</button>{" "}
<button onClick={() => gotoPage(controlledPageCount - 1)} disabled={!canNextPage}>
{">>"}
</button>{" "}
<span>
Page{" "}
<strong>
{pageIndex + 1} of {pageOptions.length}
</strong>{" "}
</span>
<span>
| Go to page:{" "}
<input
type="number"
defaultValue={pageIndex + 1}
onChange={(e) => {
const page = e.target.value ? Number(e.target.value) - 1 : 0;
gotoPage(page);
}}
style={{ width: "100px" }}
/>
</span>{" "}
<select
value={pageSize}
onChange={(e) => {
setPageSize(Number(e.target.value));
}}
>
{[10, 20, 30, 40, 50].map((pageSize) => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</div>
</>
);
}
function App() {
const [data, setData] = useState([]);
const [loading, setLoading] = React.useState(false);
const [pageCount, setPageCount] = React.useState(0);
const fetchIdRef = React.useRef(0);
const sortIdRef = React.useRef(0);
const columns = React.useMemo(
() => [
{
Header: "First Name",
accessor: "firstName",
},
{
Header: "Last Name",
accessor: "lastName"
}
],
[]
);
const fetchData = useCallback(({ sortBy, pageIndex, pageSize }) => {
if (sortBy) {
// Give this sort an ID
const sortId = ++sortIdRef.current;
// Set the loading state
setLoading(true);
//simulate remove server sort
setTimeout(() => {
// Doing multisort
if (sortId === sortIdRef.current) {
let sorted = serverData.slice();
sorted.sort((a, b) => {
for (let i = 0; i < sortBy.length; ++i) {
if (a[sortBy[i].id] > b[sortBy[i].id])
return sortBy[i].desc ? -1 : 1;
if (a[sortBy[i].id] < b[sortBy[i].id])
return sortBy[i].desc ? 1 : -1;
}
return 0;
});
const startRow = pageSize * pageIndex;
const endRow = startRow + pageSize;
setData(sorted.slice(startRow, endRow));
console.log(sorted.slice(0, 10));
setLoading(false);
}
}, 200);
} else {
// This will get called when the table needs new data
// You could fetch your data from literally anywhere,
// even a server. But for this example, we'll just fake it.
// Give this fetch an ID
const fetchId = ++fetchIdRef.current;
// Set the loading state
setLoading(true);
// We'll even set a delay to simulate a server here
setTimeout(() => {
// Only update the data if this is the latest fetch
if (fetchId === fetchIdRef.current) {
const startRow = pageSize * pageIndex;
const endRow = startRow + pageSize;
setData(serverData.slice(startRow, endRow));
// Your server could send back total page count.
// For now we'll just fake it, too
setPageCount(Math.ceil(serverData.length / pageSize));
setLoading(false);
}
}, 1000);
}
}, []);
return (
<Table
columns={columns}
data={data}
fetchData={fetchData}
loading={loading}
pageCount={pageCount}
/>
);
}
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment