Skip to content

Conversation

@arminmeh
Copy link
Contributor

@arminmeh arminmeh commented May 6, 2025

Closes #17626

v7 ignored undefined values for the rowCount while fetching children
https://github.com/mui/mui-x/blob/v7.x/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSource.ts#L185

v8 sets it to -1

I think that we can remove this altogether as it does not make sense to update root row count while updating children.
If you do, you don't have a mean to update the data via the same response, so your count cannot be correct.

@arminmeh arminmeh requested a review from a team May 6, 2025 08:53
@arminmeh arminmeh added type: bug It doesn't behave as expected. scope: data grid Changes related to the data grid. plan: Pro Impact at least one Pro user. feature: Server integration Better integration with backends, e.g. data source labels May 6, 2025
@mui-bot
Copy link

mui-bot commented May 6, 2025

Deploy preview: https://deploy-preview-17711--material-ui-x.netlify.app/

Bundle size report

Total Size Change: ▼-16B(0.00%) - Total Gzip Change: ▼-29B(0.00%)
Files: 122 total (0 added, 0 removed, 4 changed)

Show details for 100 more bundles (22 more not shown)

@mui/x-data-grid-premiumparsed: ▼-4B(0.00%) gzip: ▼-6B(0.00%)
@mui/x-data-grid-premium/DataGridPremiumparsed: ▼-4B(0.00%) gzip: ▼-6B(0.00%)
@mui/x-data-grid-proparsed: ▼-4B(0.00%) gzip: ▼-8B(-0.01%)
@mui/x-data-grid-pro/DataGridProparsed: ▼-4B(0.00%) gzip: ▼-9B(-0.01%)
@mui/x-chartsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-proparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/BarChartProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/ChartContainerProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/ChartDataProviderProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/ChartsToolbarProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/ChartZoomSliderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/FunnelChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/Heatmapparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/LineChartProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/PieChartProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/RadarChartProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts-pro/ScatterChartProparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/BarChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartContainerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartDataProviderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsAxisparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsAxisHighlightparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsClipPathparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsGridparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsLabelparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsLegendparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsLocalizationProviderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsOverlayparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsReferenceLineparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsSurfaceparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsTextparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsTooltipparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsXAxisparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ChartsYAxisparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/Gaugeparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/LineChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/PieChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/RadarChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/ScatterChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/SparkLineChartparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-charts/Toolbarparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-data-gridparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-data-grid/DataGridparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickersparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-proparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterDateFnsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterDateFnsJalaliparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterDayjsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterLuxonparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterMomentparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterMomentHijriparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/AdapterMomentJalaaliparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DateRangeCalendarparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DateRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DateRangePickerDayparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DateRangePickerDay2parsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DateTimeRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DesktopDateRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DesktopDateTimeRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/DesktopTimeRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/LocalizationProviderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/MobileDateRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/MobileDateTimeRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/MobileTimeRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/MultiInputDateRangeFieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/MultiInputDateTimeRangeFieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/MultiInputTimeRangeFieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/PickersRangeCalendarHeaderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/SingleInputDateRangeFieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/SingleInputDateTimeRangeFieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/SingleInputTimeRangeFieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/StaticDateRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers-pro/TimeRangePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterDateFnsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterDateFnsBaseparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterDateFnsJalaliparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterDayjsparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterLuxonparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterMomentparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterMomentHijriparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/AdapterMomentJalaaliparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DateCalendarparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DateFieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DatePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DateTimeFieldparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DateTimePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DayCalendarSkeletonparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DesktopDatePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DesktopDateTimePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DesktopTimePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/DigitalClockparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/LocalizationProviderparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/MobileDatePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/MobileDateTimePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/MobileTimePickerparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/MonthCalendarparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/MultiSectionDigitalClockparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/PickerDay2parsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/PickersActionBarparsed: 0B(0.00%) gzip: 0B(0.00%)
@mui/x-date-pickers/PickersCalendarHeaderparsed: 0B(0.00%) gzip: 0B(0.00%)

Details of bundle changes

Generated by 🚫 dangerJS against d81d8f9

Copy link
Member

@michelengelen michelengelen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM :shipit:

@michelengelen
Copy link
Member

Would this change impact the infinite loading feature?

https://mui.com/x/react-data-grid/server-side-data/lazy-loading/#infinite-loading

Copy link
Member

@MBilalShafi MBilalShafi May 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, there are a couple of reasons why updating the rowCount during child data fetches would make sense:

  1. Consistency in function signatures: Our data fetching function (getRows) is utilized for both root and nested data. Its return type includes rowCount, so from a TypeScript perspective, it should accept and process rowCount regardless of the data’s nesting level. Changing this without any clear indication may cause slightly unpleasant DX.
  2. Dynamic data use-case: In scenarios involving dynamic data, the root rowCount might change in the background. Allowing updates to the root rowCount alongside nested data fetches can eliminate the need for an additional API call solely to retrieve the updated row count. It may particularly help specific cases where API calls are expensive.

How about reverting to the v7 behavior—where rowCount updates are ignored if passed as undefined (except during the initial getRows call)? Do you foresee some potential issues with this approach?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about giving users an option to control this behavior—perhaps something like an ignoreRowCountOnChildFetch parameter? It doesn't have to be that exact name, but something along those lines to provide flexibility.

Copy link
Contributor Author

@arminmeh arminmeh May 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, there are a couple of reasons why updating the rowCount during child data fetches would make sense:

  1. Consistency in function signatures: Our data fetching function (getRows) is utilized for both root and nested data. Its return type includes rowCount, so from a TypeScript perspective, it should accept and process rowCount regardless of the data’s nesting level. Changing this without any clear indication may cause slightly unpleasant DX.
  2. Dynamic data use-case: In scenarios involving dynamic data, the root rowCount might change in the background. Allowing updates to the root rowCount alongside nested data fetches can eliminate the need for an additional API call solely to retrieve the updated row count. It may particularly help specific cases where API calls are expensive.

How about reverting to the v7 behavior—where rowCount updates are ignored if passed as undefined (except during the initial getRows call)? Do you foresee some potential issues with this approach?

Agree with the point about the consistency.

Taking all of this into the consideration, I think that the right behavior then would be to consider the whole response to carry the information related to the request context.
For root calls both are related to the top level. For children request, both are related to that nested level.
We do this for rows, but we consider rowCount to always be related to the top level.

If we would adopt the new behavior, we would need to use rowCount to update parent row descendants count and this looks like something you might need to do.

To prevent breaking change, new behavior can be behind an experimental flag and for now we do

How about reverting to the v7 behavior—where rowCount updates are ignored if passed as undefined (except during the initial getRows call)? Do you foresee some potential issues with this approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about giving users an option to control this behavior—perhaps something like an ignoreRowCountOnChildFetch parameter? It doesn't have to be that exact name, but something along those lines to provide flexibility.

This can also easily be done by caching the last row count on your own and returning it if the new response does not have this value

Copy link
Member

@MBilalShafi MBilalShafi May 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For root calls both are related to the top level. For children request, both are related to that nested level.
We do this for rows, but we consider rowCount to always be related to the top level.

If we would adopt the new behavior, we would need to use rowCount to update parent row descendants count and this looks like something you might need to do.

Exactly, we'll have to do it with #14527 anyways. Though I had in my mind a slightly different interface, to allow passing both the values at any nested level.

Something like:

type GetRowsResponse = {
  rows: GridValidRowModel[];
  /**
    Row count for the current nested level. For the root level, it can be omitted, or `rowCount` can be repeated.
  */
  rowCount?: number;
  /**
    Row count for the root level. It can be omitted for a particular nested level, in which case the previously set value will be used. If it's not defined in the state, it will be initialized as `-1` to represent unknown.
  */
  rootRowCount?: number;
}

It will provide a predictable structure with the flexibility to:

  1. Update the rootRowCount at any time if needed, or skip if not.
  2. Update the nested level row count if needed, to be used with cases like [data grid] Implement server-side data source with nested data lazy loading #14527
  3. Optional types make the behavior controllable by prop/API, ignoring undefined values.

To me, the current behavior of having the previous rowCount replaced by -1 if no row count is provided with any API call is unexpected. I'd advocate for only keeping it on the very first data source call.

To prevent breaking change, new behavior can be behind an experimental flag

We could possibly avoid the breaking change by adjusting the naming, so we keep the rowCount property and add a new one for the current nested level, such as currentRowCount.
The in v9, we do a breaking change rowCount => rootRowCount and currentRowCount => rowCount, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the behavior to be the same as in v7 to avoid breaking change.

We could possibly avoid the breaking change by adjusting the naming, so we keep the rowCount property and add a new one for the current nested level, such as currentRowCount.
The in v9, we do a breaking change rowCount => rootRowCount and currentRowCount => rowCount, wdyt?

I agree that we will need a new property when #14527 is implemented, but I will leave that to be added in the related PR.

From v9 I think that we should do the replacement currentRowCount => rowCount (once it is added) and not have root row count processed with the children data fetch (not doing rowCount => rootRowCount).

For me it is weird to get back information that is not related to my request. I have asked for the children of a specific parent. I don't expect to get back the root row count. Can you find an example of an API that does something like this?

In addition to this, providing a root row count without root rows is just half of the data I need to actually refresh my tree.

@github-actions
Copy link

This pull request has been inactive for 30 days. Please remove the stale label or leave a comment to keep it open. Otherwise, it will be closed in 15 days.

@github-actions github-actions bot added the stale Inactive for 7 days (issues) or 30 days (PRs); closed after 5 or 15 more days if no update. label Jun 16, 2025
@arminmeh arminmeh removed the stale Inactive for 7 days (issues) or 30 days (PRs); closed after 5 or 15 more days if no update. label Jun 16, 2025
@arminmeh arminmeh force-pushed the ignore-row-count-while-loading-children branch from 87790dd to e8c54e9 Compare June 16, 2025 09:22
@arminmeh arminmeh changed the title [DataGridPro] Ignore rowCount response when new children are fetched with the data source [DataGridPro] Ignore missing rowCount response when new children are fetched with the data source Jun 16, 2025
@arminmeh arminmeh requested a review from MBilalShafi June 16, 2025 09:23
@arminmeh arminmeh merged commit 922ad56 into mui:master Jun 19, 2025
22 checks passed
@arminmeh arminmeh deleted the ignore-row-count-while-loading-children branch June 19, 2025 11:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: Server integration Better integration with backends, e.g. data source plan: Pro Impact at least one Pro user. scope: data grid Changes related to the data grid. type: bug It doesn't behave as expected.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[data grid] rowCount set to -1 when fetching child rows in server-side Tree Data

5 participants