I followed this mantine data table boilerplate to fit with tanstack query way of fetching paginated table data but as the title says....
here are the hooks
import { PAGE_SIZES } from "@/data/smba/data";
import { call, Answer, mk, baseUrlSmba, globalRetry, globalStaleTime } from "@/utils/globalApiHandler";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import { DataTableSortStatus, differenceBy, uniqBy } from "mantine-datatable";
import { useEffect, useState } from "react";
import { MataPelajaran } from "./mata-pelajaran-types";
import { queryClient } from "@/pages/_app";
export function useGetMataPelajaran() {
// parameters
const [sortStatus, setSortStatus] = useState<DataTableSortStatus>({
columnAccessor: "created_at",
direction: "asc",
});
const [pageSize, setPageSize] = useState<number>(PAGE_SIZES[0]);
const [page, setPage] = useState<number>(1);
const [keyword, setKeyword] = useState<string>("");
const [totalRecords, setTotalRecords] = useState<number>(0);
// special for this endpoint because of select all with pagination shenanigans
const [records, setRecords] = useState<MataPelajaran[]>([]);
// row selection
const [selectAll, setSelectAll] = useState<boolean>(false);
const [select, setSelect] = useState<MataPelajaran[]>([]);
const [unselect, setUnselect] = useState<MataPelajaran[]>([]);
// reset to page 1 on search
useEffect(() => {
if (keyword !== "") {
setPage(1);
}
}, [keyword]);
// fetch
const queryParams: string = `?sort=${sortStatus.direction}&order_by=${sortStatus.columnAccessor}&page=${page}&per_page=${pageSize}&keyword=${keyword}`;
const { data, isPending, isError, error, isPlaceholderData } = useQuery<Answer<MataPelajaran[]>>({
queryKey: ["mata-pelajaran", sortStatus, pageSize, page, keyword, totalRecords],
queryFn: () => call(mk.get, baseUrlSmba + "/master/mata-pelajaran" + queryParams),
retry: globalRetry,
placeholderData: keepPreviousData,
staleTime: globalStaleTime,
});
// pagination pre-fetching
useEffect(() => {
if (!isPlaceholderData && data?.metadata && data?.metadata?.page_count > 1) {
queryClient.prefetchQuery({
queryKey: ["mata-pelajaran", page + 1],
queryFn: () => call(mk.get, baseUrlSmba + "/master/mata-pelajaran" + queryParams),
});
}
}, [data?.metadata, isPlaceholderData, page, queryParams]);
// slicing the data array for select all with pagination handling
useEffect(() => {
const slicedData = data?.data?.slice(0, PAGE_SIZES[0]) ?? [];
setRecords(slicedData);
}, [data?.data]);
// selection handling
function handleSelectChange(newSelection: MataPelajaran[]) {
if (selectAll) {
const toBeUnselected = records.filter((record) => !newSelection.includes(record)) ?? [];
setUnselect(uniqBy([...unselect, ...toBeUnselected], (record) => record.id).filter((record) => !newSelection.includes(record)));
} else {
setSelect(newSelection);
}
}
// select all handling
function handleSelectAllChange() {
if (!selectAll) {
setSelectAll(true);
setSelect(records);
setUnselect([]);
} else {
setSelectAll(false);
setSelect([]);
setUnselect([]);
}
}
// I don't know, boilerplate from mantine data table tailored to my requirements
useEffect(() => {
const from = (page - 1) * PAGE_SIZES[0];
const to = from + PAGE_SIZES[0];
const currentRecords = records.slice(from, to);
if (selectAll) {
differenceBy(currentRecords, unselect, (record) => record.id);
}
}, [unselect, selectAll, page, records]);
// params handling
useEffect(() => {
if (data && data?.metadata) {
setTotalRecords(data?.metadata?.total_count);
setPage(data?.metadata?.page);
setPageSize(data?.metadata?.per_page || PAGE_SIZES[0]);
setKeyword(data?.metadata?.keyword || "");
}
}, [data, setPage, setPageSize, setTotalRecords, setKeyword]);
return {
data,
isPending,
isError,
error,
pageSize,
setPageSize,
page,
setPage,
sortStatus,
setSortStatus,
keyword,
setKeyword,
totalRecords,
setTotalRecords,
records,
selectAll,
setSelectAll,
select,
setSelect,
unselect,
setUnselect,
handleSelectChange,
handleSelectAllChange,
};
}
and here's the template (the texts from allRecordsSelectionCheckboxProps does not even render)
<DataTable
className="custom-table"
withColumnBorders
highlightOnHover
shadow="sm"
selectedRecords={select}
onSelectedRecordsChange={handleSelectChange}
allRecordsSelectionCheckboxProps={{
indeterminate: selectAll && !!unselect.length,
checked: selectAll,
onChange: handleSelectAllChange,
title: selectAll ? "Unselect all records" : `Select ${mpData?.metadata?.total_count} records`,
}}
records={records}
columns={columns}
fetching={mpIsPending}
totalRecords={mpTotalRecords}
recordsPerPage={mpPageSize}
page={mpPage}
paginationSize="sm"
onPageChange={(p) => mpSetPage(p)}
recordsPerPageOptions={PAGE_SIZES}
onRecordsPerPageChange={mpSetPageSize}
paginationText={({ from, to, totalRecords }) => `Records ${from} - ${to} of ${totalRecords}`}
/>
I tried to make them checkboxes on all rows on all pages tick and expects that to happen but seems like the nature curses me.
Here's an interesting thing. When I click on the check all check box on an active current page, it ticks all the rows of the current page, several rows of the neighboring pages, then further and further they're getting less ticked and then none (I don't know if this makes sense but I hope you can imagine it based on my description)