Why is it when I try to select all the rows, they only select the rows from the current active page while I need it to select all rows from all pages?

14 views Asked by At

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)

0

There are 0 answers