Description
Steps to reproduce
https://codesandbox.io/p/sandbox/twilight-bush-wk8glc
Current behavior
When using MUI DataGrid Pro with paginationMode="server" and rowCount={-1} (to indicate an unknown total count as per the official documentation), the pagination display behaves incorrectly.
What happens:
The label shows values like "from -25 to 0 of many" instead of a valid range.
Pagination behaves in reverse: it allows going backwards, but not forwards.
The current page index can become negative, which breaks the pagination logic.
Expected behavior
The label should correctly show something like "from X to Y of many", where X and Y are based on the current page and page size.
Pagination should allow forward navigation, and never display or enter negative pages.
Context
Below is a simplified version of the relevant parts of my implementation:
STATES:
const [paginationModel, setPaginationModel] = useState({
page: 0,
pageSize: 100,
filters: [],
pre_filters: [],
super_filter: [],
sort: {},
selectedGroup: null,
});
const [hasNextPage, setHasNextPage] = useState(undefined);
const paginationMetaRef = useRef({ hasNextPage: undefined });
FETCH
const getRows = useCallback(async () => {
try {
setHasNextPage(undefined);
setIsLoading(true);
const { data } = await axiosInstance.post(`${BASE_URL}/rows`, {
...paginationModel,
workspace_id: workspace_source,
});
setHasNextPage(data.rows.length >= paginationModel.pageSize);
setRows(data.rows);
setRowCount(data.totalCount); // ⚠️ could be -1 when unknown
} catch (error) {
toast.error(error.message || 'Errore nel recupero dei dati');
setRows([]);
setRowCount(0);
} finally {
setIsLoading(false);
}
}, [BASE_URL, paginationModel, workspace_source]);
useEffect(() => {
getRows();
}, [getRows]);
PAGINATION META
const paginationMeta = useMemo(() => {
if (
hasNextPage !== undefined &&
paginationMetaRef.current?.hasNextPage !== hasNextPage
) {
paginationMetaRef.current = { hasNextPage };
}
return paginationMetaRef.current;
}, [hasNextPage]);
DATAGRID
<DataGridPro
apiRef={apiRef}
slots={{
pagination: CustomPagination,
toolbar: DiscoveryToolbar,
}}
sx={{
flexGrow: 1,
width: '100%',
'& .MuiDataGrid-main': { flexGrow: 1 },
}}
rows={rows}
columns={columnsState.columns}
onColumnWidthChange={columnsState.onColumnWidthChange}
onColumnOrderChange={columnsState.onColumnOrderChange}
rowCount={rowCount}
loading={isLoading}
getRowClassName={(params) =>
username.includes(params.row.downloaded_by) ? 'downloaded' : params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
}
slotProps={{
panel: {
anchorEl: buttonRef,
},
pagination: {
labelDisplayedRows: ({ from, to, count, ...props }) => {
console.log("props", props)
return ${fNumber(from)}-${fNumber(to)} of ${count === -1 ? 'many' : fNumber(count)}
}
},
toolbar: {
getData: getRows,
section_id: crom-disovery-default,
setButtonRef,
apiRef,
BASE_URL,
paginationModel,
rowCount,
groupByOptions,
setSelectedGroup,
openFilters,
setOpenFilters,
detailsColumns: discoveryInfo.find((item) => item.type ===
'default_discovery_columns')?.columns || [],
rowSelectionModel,
workspace_source,
current_analysis_type: null,
workspaceInfo
},
headerFilterCell: {
InputComponentProps: {
size: 'small',
},
},
row: {
onContextMenu: handleContextMenu,
style: { cursor: 'context-menu' },
},
}}
initialState={{
density: 'compact',
}}
density='compact'
// PAGINATION
paginationMeta={paginationMeta}
paginationModel={paginationModel}
onPaginationModelChange={handlePaginationModelChange}
paginationMode="server"
pageSizeOptions={[100, 200, 500]}
pagination
// ROW ID
getRowId={(row) => paginationModel.selectedGroup ? JSON.stringify(row._id) : row?._id?.$oid}
// FILTERS
unstable_headerFilters
filterMode="server"
onFilterModelChange={onFilterChange}
// SORTING
sortingMode="server"
onSortModelChange={handleSortModelChange}
// SELECTION
rowSelectionModel={rowSelectionModel}
checkboxSelection={paginationModel.selectedGroup === null}
disableRowSelectionOnClick
keepNonExistentRowsSelected
onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel);
}}
/>
CUSTOM PAGINATION
function Pagination(props) {
const { page, onPageChange, className } = props;
⚠️ page is set to -1 IDK WHY
const apiRef = useGridApiContext();
const pageCount = useGridSelector(apiRef, gridPageCountSelector);
return (
<MuiPagination
color="primary"
className={className}
count={pageCount}
page={page + 1}
onChange={(event, newPage) => {
console.log("newPage", newPage)
onPageChange(event, newPage - 1);
}}
/>
);
}
function CustomPagination(props) {
return <GridPagination ActionsComponent={Pagination} {...props} />;
}
Your environment
npx @mui/envinfo
System:
OS: Linux 5.15 Ubuntu 22.04.5 LTS 22.04.5 LTS (Jammy Jellyfish)
Binaries:
Node: 20.12.2 - ~/.nvm/versions/node/v20.12.2/bin/node
npm: 10.9.2 - ~/webapps/apps.convey.tools/convey-apps/frontend/node_modules/.bin/npm
pnpm: Not Found
Browsers:
Chrome: Not Found
npmPackages:
@emotion/react: ^11.13.3 => 11.14.0
@emotion/styled: ^11.13.0 => 11.14.0
@mui/base: ^5.0.0-beta.40 => 5.0.0-beta.40
@mui/core-downloads-tracker: 6.4.5
@mui/icons-material: ^6.1.2 => 6.1.2
@mui/lab: ^5.0.0-alpha.134 => 5.0.0-alpha.166
@mui/material: ^6.1.6 => 6.4.5
@mui/private-theming: 5.15.12
@mui/styled-engine: 6.1.6
@mui/styled-engine-sc: ^6.1.6 => 6.1.6
@mui/styles: ^5.15.12 => 5.15.12
@mui/system: ^6.1.6 => 6.1.6
@mui/types: 7.2.21
@mui/utils: 6.1.6
@mui/x-charts: ^7.22.2 => 7.22.2
@mui/x-charts-vendor: 7.20.0
@mui/x-data-grid: 6.20.4
@mui/x-data-grid-pro: ^6.18.7 => 6.20.4
@mui/x-date-pickers: ^7.1.0 => 7.1.0
@mui/x-date-pickers-pro: ^7.1.0 => 7.27.0
@mui/x-internals: 7.26.0
@mui/x-license: 7.26.0
@mui/x-license-pro: ^6.10.2 => 6.10.2
@mui/x-tree-view: ^7.23.0 => 7.23.0
@types/react: ^18.2.12 => 18.2.60
react: ^18.2.0 => 18.2.0
react-dom: ^18.2.0 => 18.2.0
styled-components: ^6.1.13 => 6.1.13
typescript: ^4.9.5 => 4.9.5
Search keywords: unkown count negative pagination
Order ID: 101883