diff --git a/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSource.ts b/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSource.ts index d13d64e1895d7..44b8a2c548d00 100644 --- a/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSource.ts +++ b/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSource.ts @@ -18,6 +18,7 @@ import { useGridRegisterStrategyProcessor, runIf, } from '@mui/x-data-grid/internals'; +import { unstable_debounce as debounce } from '@mui/utils'; import { GridPrivateApiPro } from '../../../models/gridApiPro'; import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; import { gridGetRowsParamsSelector, gridDataSourceErrorsSelector } from './gridDataSourceSelector'; @@ -323,6 +324,8 @@ export const useGridDataSource = ( [apiRef], ); + const debouncedFetchRows = React.useMemo(() => debounce(fetchRows, 0), [fetchRows]); + const handleStrategyActivityChange = React.useCallback< GridEventListener<'strategyAvailabilityChange'> >(() => { @@ -385,17 +388,17 @@ export const useGridDataSource = ( useGridApiEventHandler( apiRef, 'sortModelChange', - runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), + runIf(defaultRowsUpdateStrategyActive, () => debouncedFetchRows()), ); useGridApiEventHandler( apiRef, 'filterModelChange', - runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), + runIf(defaultRowsUpdateStrategyActive, () => debouncedFetchRows()), ); useGridApiEventHandler( apiRef, 'paginationModelChange', - runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), + runIf(defaultRowsUpdateStrategyActive, () => debouncedFetchRows()), ); const isFirstRender = React.useRef(true); diff --git a/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts b/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts index c36a874f5ef82..91dcc8c7ee6f1 100644 --- a/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts +++ b/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts @@ -1,5 +1,6 @@ import * as React from 'react'; import { throttle } from '@mui/x-internals/throttle'; +import { unstable_debounce as debounce } from '@mui/utils'; import { useGridApiEventHandler, useGridSelector, @@ -415,6 +416,15 @@ export const useGridDataSourceLazyLoader = ( [props.unstable_lazyLoadingRequestThrottleMs, handleRenderedRowsIntervalChange], ); + const debouncedFetchRows = React.useCallback( + (params: Partial) => + debounce( + () => privateApiRef.current.unstable_dataSource.fetchRows(GRID_ROOT_GROUP_ID, params), + 0, + ), + [privateApiRef], + ); + const handleGridSortModelChange = React.useCallback>( (newSortModel) => { rowsStale.current = true; @@ -438,12 +448,9 @@ export const useGridDataSourceLazyLoader = ( }; privateApiRef.current.setLoading(true); - privateApiRef.current.unstable_dataSource.fetchRows( - GRID_ROOT_GROUP_ID, - adjustRowParams(getRowsParams), - ); + debouncedFetchRows(adjustRowParams(getRowsParams)); }, - [privateApiRef, filterModel, paginationModel.pageSize, adjustRowParams], + [privateApiRef, filterModel, paginationModel.pageSize, adjustRowParams, debouncedFetchRows], ); const handleGridFilterModelChange = React.useCallback>( @@ -458,9 +465,9 @@ export const useGridDataSourceLazyLoader = ( }; privateApiRef.current.setLoading(true); - privateApiRef.current.unstable_dataSource.fetchRows(GRID_ROOT_GROUP_ID, getRowsParams); + debouncedFetchRows(getRowsParams); }, - [privateApiRef, sortModel, paginationModel.pageSize], + [privateApiRef, sortModel, paginationModel.pageSize, debouncedFetchRows], ); const handleStrategyActivityChange = React.useCallback< diff --git a/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx index fbbc993501f36..3ed6b33ada0ce 100644 --- a/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx @@ -125,6 +125,21 @@ describeSkipIf(isJSDOM)(' - Data source', () => { }); }); + it('should re-fetch the data once if multiple models have changed', async () => { + const { setProps } = render(); + await waitFor(() => { + expect(fetchRowsSpy.callCount).to.equal(1); + }); + + setProps({ paginationModel: { page: 1, pageSize: 10 } }); + setProps({ sortModel: [{ field: 'name', sort: 'asc' }] }); + setProps({ filterModel: { items: [{ field: 'name', value: 'John', operator: 'contains' }] } }); + + await waitFor(() => { + expect(fetchRowsSpy.callCount).to.equal(2); + }); + }); + describe('Cache', () => { it('should cache the data using the default cache', async () => { render(); diff --git a/packages/x-data-grid-pro/src/tests/dataSourceLazyLoader.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/dataSourceLazyLoader.DataGridPro.test.tsx index 9cab47e58440a..3602f55331aad 100644 --- a/packages/x-data-grid-pro/src/tests/dataSourceLazyLoader.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/dataSourceLazyLoader.DataGridPro.test.tsx @@ -85,6 +85,20 @@ describeSkipIf(isJSDOM)(' - Data source lazy loader', () => { }); }); + it('should re-fetch the data once if multiple models have changed', async () => { + const { setProps } = render(); + await waitFor(() => { + expect(fetchRowsSpy.callCount).to.equal(1); + }); + + setProps({ sortModel: [{ field: 'name', sort: 'asc' }] }); + setProps({ filterModel: { items: [{ field: 'name', value: 'John', operator: 'contains' }] } }); + + await waitFor(() => { + expect(fetchRowsSpy.callCount).to.equal(2); + }); + }); + describe('Viewport loading', () => { it('should render skeleton rows if rowCount is bigger than the number of rows', async () => { render();