material-react-table icon indicating copy to clipboard operation
material-react-table copied to clipboard

Manual pagination onPaginationChange triggers twice, resetting pageIndex to zero

Open ponbac opened this issue 1 year ago • 9 comments

material-react-table version

3.0.1

react & react-dom versions

18.3.1

Describe the bug and the steps to reproduce it

Described by someone on StackOverflow here: https://stackoverflow.com/questions/79003280/material-react-table-pagination-not-working-as-expected-manual-pagination-unwant

Setting the same pagination state in initialState fixes the problem for me. It feels like the table instance gets recreated when it shouldn't? Sorry if the problem is on my part.

Minimal, Reproducible Example - (Optional, but Recommended)

From the StackOverflow post, my code is similar:

const UsersComponents = ({ showCreateUser, setShowCreateUser }) => {
  const theme = useTheme();
  const [validationErrors, setValidationErrors] = useState({});
  
  const [tableData, setTableData] = useState([]);
  const [pagination, setPagination] = useState({
    pageIndex: 0,
    pageSize: 10,
  });

  const handlePaginationChange = (updater) => {
    setPagination((oldPagination) => {
      const newPagination =
        typeof updater === "function" ? updater(oldPagination) : updater;

      console.log("Updated pagination:", newPagination); // Debug the new pagination state
      return newPagination;
    });
  };


  const queryClient = useQueryClient();

  const {
    data: fetchedUsers = [],
    isError: isLoadingUsersError,
    isFetching: isFetchingUsers,
    refetch,
  } = useQuery({
    queryKey: ["users", pagination.pageIndex, pagination.pageSize],
    refetchOnWindowFocus: false,
    placeholderData: keepPreviousData,
    queryFn: async () => {
      const response = await fetchUsers({
        offset: pagination.pageIndex * pagination.pageSize,
        limit: pagination.pageSize,
      });
      console.log(response, "response chk");
      return response;
    },
  });

  const { data: userCount = 0 } = useQuery({
    queryKey: ["totalCount"],
    queryFn: fetchUserCount,
    refetchOnWindowFocus: false,
  });

  
  const tableColumns = useMemo(
    () => columns(validationErrors, setValidationErrors),
    [validationErrors]
  );


  const table = useMaterialReactTable({
    columns: tableColumns,
    data: fetchedUsers,
    pageCount: Math.ceil(userCount / pagination.pageSize),
    onPaginationChange: handlePaginationChange,
    pagination,
    muiPaginationProps: {
      color: "primary",
      shape: "rounded",
      showRowsPerPage: false,
      variant: "outlined",
    },
    mrtTheme: (theme) => ({
      baseBackgroundColor: theme.palette.background.paper,
    }),
    manualPagination: true,
    createDisplayMode: "modal", //default ('row', and 'custom' are also available)
    editDisplayMode: "row", //default ('row', 'cell', 'table', and 'custom' are also available)
    enableEditing: true,
    paginationDisplayMode: "pages",
    paginateExpandedRows: true,
    enableRowSelection: true,
    enableFullScreenToggle: false,
    enableDensityToggle: false,
    columnFilterDisplayMode: "popover",
    positionToolbarAlertBanner: "bottom",
    positionGlobalFilter: "left",
    getRowId: (row) => row.id,
    muiSearchTextFieldProps: {
      placeholder: "Search all users",
      variant: "outlined",
    },
    muiToolbarAlertBannerProps: isLoadingUsersError
      ? {
          color: "error",
          children: "Error loading data",
        }
      : undefined,
    muiTableContainerProps: {
      sx: {
        minHeight: "500px",
      },
    },
    
    state: {
      pagination,
      isLoading: isFetchingUsers,
      isSaving: isCreatingUser || isUpdatingUser || isDeletingUser,
      showAlertBanner: isLoadingUsersError,
      showProgressBars: isFetchingUsers,
    },
  });

  return (
    <>
      <MaterialReactTable table={table} />
    </>
  );
};

const queryClient = new QueryClient();

const UserManagement = ({ showCreateUser, setShowCreateUser }) => (
  <QueryClientProvider client={queryClient}>
    <UsersComponents
      showCreateUser={showCreateUser}
      setShowCreateUser={setShowCreateUser}
    />
  </QueryClientProvider>
);

export default UserManagement;

Screenshots or Videos (Optional)

No response

Do you intend to try to help solve this bug with your own PR?

None

Terms

  • [X] I understand that if my bug cannot be reliably reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.

ponbac avatar Sep 23 '24 09:09 ponbac

Hi, same issue on my side as well...

Zemelia avatar Oct 29 '24 18:10 Zemelia

I have the same issue with v3.1.0 onPaginationChange triggers twice when number of items in last page < pageSize. onPaginationChange triggers once when number of items in last page == pageSize. Please help me fix it! Many thanks

haiau avatar Dec 23 '24 19:12 haiau

Hi, i have the same issue as above, iv recently moved over to manual pagination, but notice when i get to the last page, it shows for a split second then resets to page when. As mentioned above, seems to be when items on page is < pageSize

CyferShepard avatar Dec 26 '24 18:12 CyferShepard

Just an update after further testing, it seems to be also when the next page is loaded and the current row length != initial pages row length does it cause the index to reset

CyferShepard avatar Dec 26 '24 22:12 CyferShepard

Another update, i found a workaround, setting rowCount to something arbitrary like a fixed value or to the pageSize seems to stop this behavior

CyferShepard avatar Dec 26 '24 23:12 CyferShepard

After follow this docs:

https://www.material-react-table.com/docs/guides/pagination#override-page-count-and-row-count https://github.com/TanStack/table/issues/411

I fixed the problem by this way:

const [totalPages, setTotalPages] = useState(0);

useEffect(() => { if (tableSize) { setTotalPages(Math.ceil(tableSize / pagination.pageSize)); } }, [tableSize, pagination.pageSize]);

<DataTable ... pageCount={totalPages} rowCount={totalPages} ... />

Note: tableSize is number records return from server

haiau avatar Dec 27 '24 06:12 haiau

Same issue here as well!

ahmedd-osama avatar May 12 '25 13:05 ahmedd-osama

I am not doing manual pagination and I am using useMaterialReactTable hook, still I am facing the same issue.

export const useMaterialReactTableConfig = (columns, data, isLoading, pageSize ) => {
  return useMaterialReactTable({
    columns,
    data,
    state: { isLoading },
    enableColumnResizing: false,
    enableFullScreenToggle: false,
    initialState: {
      density: "compact",
      pagination: {
        pageSize: pageSize,
        pagination: { pageSize: 10, pageIndex: 0 },
      },
    },
    renderEmptyRowsFallback: () => <NoData />,
    muiTableBodyProps: {
      sx: {
        "& tr:nth-of-type(odd)": {
          backgroundColor: "#f5f5f5",
        },
      },
    },
    muiTableContainerProps: {
      sx: { maxHeight: "500px", boxShadow: "none", borderRadius: 0 },
    },
    muiTablePaperProps: {
      sx: {
        boxShadow: 0,
        borderRadius: 0,
        height: "100%",
      },
    },
  });
}

  const columns = useMemo(() => getAttendanceColumns(setData), [setData]);

  const table = useMaterialReactTableConfig(columns,data,isLoading, pageSize);

<MaterialReactTable table={table} />

akashutosh77 avatar Oct 11 '25 06:10 akashutosh77

I have similar issue when I have 2 tables on the same page, but in diff tabs If 1 table has less number of rowsCount than other, so the library will trigger pagination update for less number of rowCount for both tables Any workaround for this behavior?

sashberd avatar Dec 11 '25 10:12 sashberd