@mui/x-data-grid-pro 8.27.1 → 8.27.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -0
- package/DataGridPro/DataGridPro.js +8 -1
- package/DataGridPro/useDataGridProProps.js +1 -0
- package/components/headerFiltering/GridHeaderFilterCell.js +1 -1
- package/esm/DataGridPro/DataGridPro.js +8 -1
- package/esm/DataGridPro/useDataGridProProps.js +1 -0
- package/esm/components/headerFiltering/GridHeaderFilterCell.js +1 -1
- package/esm/hooks/features/dataSource/useGridDataSourceBasePro.d.ts +1 -1
- package/esm/hooks/features/dataSource/useGridDataSourceBasePro.js +36 -20
- package/esm/hooks/features/dataSource/utils.d.ts +3 -1
- package/esm/hooks/features/dataSource/utils.js +14 -7
- package/esm/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.d.ts +1 -1
- package/esm/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.js +176 -65
- package/esm/index.js +1 -1
- package/hooks/features/dataSource/useGridDataSourceBasePro.d.ts +1 -1
- package/hooks/features/dataSource/useGridDataSourceBasePro.js +36 -20
- package/hooks/features/dataSource/utils.d.ts +3 -1
- package/hooks/features/dataSource/utils.js +14 -7
- package/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.d.ts +1 -1
- package/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.js +176 -65
- package/index.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,111 @@
|
|
|
5
5
|
All notable changes to this project will be documented in this file.
|
|
6
6
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
7
7
|
|
|
8
|
+
## 8.27.3
|
|
9
|
+
|
|
10
|
+
_Feb 25, 2026_
|
|
11
|
+
|
|
12
|
+
We'd like to extend a big thank you to the 4 contributors who made this release possible. Here are some highlights ✨:
|
|
13
|
+
|
|
14
|
+
- 🐞 Bugfixes
|
|
15
|
+
- ⚡️ Improved dynamic data support and cache invalidation in lazy loading for Data Grid Pro
|
|
16
|
+
|
|
17
|
+
The following team members contributed to this release:
|
|
18
|
+
@cherniavskii, @michelengelen, @MBilalShafi, @arminmeh
|
|
19
|
+
|
|
20
|
+
### Data Grid
|
|
21
|
+
|
|
22
|
+
#### `@mui/x-data-grid@8.27.3`
|
|
23
|
+
|
|
24
|
+
- [DataGrid] Preserve key input during row edit when using `rowModesModel` (#21457) @michelengelen
|
|
25
|
+
|
|
26
|
+
#### `@mui/x-data-grid-pro@8.27.3` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
27
|
+
|
|
28
|
+
Same changes as in `@mui/x-data-grid@8.27.3`, plus:
|
|
29
|
+
|
|
30
|
+
- [DataGridPro] Improve dynamic data support and cache invalidation in lazy loading (#21465) @MBilalShafi
|
|
31
|
+
|
|
32
|
+
#### `@mui/x-data-grid-premium@8.27.3` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
33
|
+
|
|
34
|
+
Same changes as in `@mui/x-data-grid-pro@8.27.3`.
|
|
35
|
+
|
|
36
|
+
### Core
|
|
37
|
+
|
|
38
|
+
- [code-infra] Do not append `x` to the last version for the compare API (#21422) @arminmeh
|
|
39
|
+
- [docs-infra] Fix current version detection logic (#21415) @cherniavskii
|
|
40
|
+
|
|
41
|
+
## 8.27.2
|
|
42
|
+
|
|
43
|
+
_Feb 20, 2026_
|
|
44
|
+
|
|
45
|
+
We'd like to extend a big thank you to the 3 contributors who made this release possible. Here are some highlights ✨:
|
|
46
|
+
|
|
47
|
+
- 🐞 Bugfixes
|
|
48
|
+
|
|
49
|
+
### Data Grid
|
|
50
|
+
|
|
51
|
+
#### `@mui/x-data-grid@8.27.2`
|
|
52
|
+
|
|
53
|
+
Internal changes.
|
|
54
|
+
|
|
55
|
+
#### `@mui/x-data-grid-pro@8.27.2` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
56
|
+
|
|
57
|
+
Same changes as in `@mui/x-data-grid@8.27.2`, plus:
|
|
58
|
+
|
|
59
|
+
- [DataGridPro] Fix number input visibility in header filters (#21345) @michelengelen
|
|
60
|
+
|
|
61
|
+
#### `@mui/x-data-grid-premium@8.27.2` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
62
|
+
|
|
63
|
+
Same changes as in `@mui/x-data-grid-pro@8.27.2`.
|
|
64
|
+
|
|
65
|
+
### Date and Time Pickers
|
|
66
|
+
|
|
67
|
+
#### `@mui/x-date-pickers@8.27.2`
|
|
68
|
+
|
|
69
|
+
- [DatePicker] Add keyboard support for selecting day, month, and year (#21399) @michelengelen
|
|
70
|
+
|
|
71
|
+
#### `@mui/x-date-pickers-pro@8.27.2` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
72
|
+
|
|
73
|
+
Same changes as in `@mui/x-date-pickers@8.27.2`, plus:
|
|
74
|
+
|
|
75
|
+
- [DateRangePicker] Fix timezone update issue leading to `invalidRange` error (#21382) @michelengelen
|
|
76
|
+
|
|
77
|
+
### Charts
|
|
78
|
+
|
|
79
|
+
#### `@mui/x-charts@8.27.2`
|
|
80
|
+
|
|
81
|
+
Internal changes.
|
|
82
|
+
|
|
83
|
+
#### `@mui/x-charts-pro@8.27.2` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
84
|
+
|
|
85
|
+
Same changes as in `@mui/x-charts@8.27.2`, plus:
|
|
86
|
+
|
|
87
|
+
- [charts-pro] Handle edge case in export image (#21206) @bernardobelchior
|
|
88
|
+
|
|
89
|
+
#### `@mui/x-charts-premium@8.27.2` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
90
|
+
|
|
91
|
+
Same changes as in `@mui/x-charts-pro@8.27.2`.
|
|
92
|
+
|
|
93
|
+
### Tree View
|
|
94
|
+
|
|
95
|
+
#### `@mui/x-tree-view@8.27.2`
|
|
96
|
+
|
|
97
|
+
- [tree view] Focus item sibling on unmount instead of the 1st item (#21386) @flaviendelangle
|
|
98
|
+
|
|
99
|
+
#### `@mui/x-tree-view-pro@8.27.2` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
100
|
+
|
|
101
|
+
Same changes as in `@mui/x-tree-view@8.27.2`.
|
|
102
|
+
|
|
103
|
+
### Codemod
|
|
104
|
+
|
|
105
|
+
#### `@mui/x-codemod@8.27.2`
|
|
106
|
+
|
|
107
|
+
Internal changes.
|
|
108
|
+
|
|
109
|
+
### Core
|
|
110
|
+
|
|
111
|
+
- [code-infra] Only ignore `renovate[bot]` in changelog generation script (#21188) @bernardobelchior
|
|
112
|
+
|
|
8
113
|
## v8.27.1
|
|
9
114
|
|
|
10
115
|
<!-- generated comparing v8.27.0..v8.x -->
|
|
@@ -34,7 +34,7 @@ const configuration = {
|
|
|
34
34
|
useFilterValueGetter: apiRef => apiRef.current.getRowValue
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
const releaseInfo = "
|
|
37
|
+
const releaseInfo = "MTc3MTk3NzYwMDAwMA==";
|
|
38
38
|
const watermark = /*#__PURE__*/(0, _jsxRuntime.jsx)(_xLicense.Watermark, {
|
|
39
39
|
packageName: "x-data-grid-pro",
|
|
40
40
|
releaseInfo: releaseInfo
|
|
@@ -192,6 +192,13 @@ DataGridProRaw.propTypes = {
|
|
|
192
192
|
get: _propTypes.default.func.isRequired,
|
|
193
193
|
set: _propTypes.default.func.isRequired
|
|
194
194
|
}),
|
|
195
|
+
/**
|
|
196
|
+
* If positive, the Data Grid will periodically revalidate data source rows by re-fetching them from the server when the cache entry has expired.
|
|
197
|
+
* If the refetched rows are different from the current rows, the grid will update the rows.
|
|
198
|
+
* Set to `0` to disable polling.
|
|
199
|
+
* @default 0
|
|
200
|
+
*/
|
|
201
|
+
dataSourceRevalidateMs: _propTypes.default.number,
|
|
195
202
|
/**
|
|
196
203
|
* If above 0, the row children will be expanded up to this depth.
|
|
197
204
|
* If equal to -1, all the row children will be expanded.
|
|
@@ -41,6 +41,7 @@ const DATA_GRID_PRO_PROPS_DEFAULT_VALUES = exports.DATA_GRID_PRO_PROPS_DEFAULT_V
|
|
|
41
41
|
treeData: false,
|
|
42
42
|
lazyLoading: false,
|
|
43
43
|
lazyLoadingRequestThrottleMs: 500,
|
|
44
|
+
dataSourceRevalidateMs: 0,
|
|
44
45
|
listView: false,
|
|
45
46
|
multipleColumnsSortingMode: 'withModifierKey',
|
|
46
47
|
pinnedColumnsSectionSeparator: 'border-and-shadow',
|
|
@@ -34,7 +34,7 @@ const StyledInputComponent = (0, _styles.styled)(_xDataGrid.GridFilterInputValue
|
|
|
34
34
|
flex: 1,
|
|
35
35
|
marginRight: _internals.vars.spacing(0.5),
|
|
36
36
|
marginBottom: _internals.vars.spacing(-0.25),
|
|
37
|
-
'& input[type="
|
|
37
|
+
'& input[type="date"], & input[type="datetime-local"]': {
|
|
38
38
|
'&[value=""]:not(:focus)': {
|
|
39
39
|
color: 'transparent'
|
|
40
40
|
}
|
|
@@ -27,7 +27,7 @@ const configuration = {
|
|
|
27
27
|
useFilterValueGetter: apiRef => apiRef.current.getRowValue
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
-
const releaseInfo = "
|
|
30
|
+
const releaseInfo = "MTc3MTk3NzYwMDAwMA==";
|
|
31
31
|
const watermark = /*#__PURE__*/_jsx(Watermark, {
|
|
32
32
|
packageName: "x-data-grid-pro",
|
|
33
33
|
releaseInfo: releaseInfo
|
|
@@ -185,6 +185,13 @@ DataGridProRaw.propTypes = {
|
|
|
185
185
|
get: PropTypes.func.isRequired,
|
|
186
186
|
set: PropTypes.func.isRequired
|
|
187
187
|
}),
|
|
188
|
+
/**
|
|
189
|
+
* If positive, the Data Grid will periodically revalidate data source rows by re-fetching them from the server when the cache entry has expired.
|
|
190
|
+
* If the refetched rows are different from the current rows, the grid will update the rows.
|
|
191
|
+
* Set to `0` to disable polling.
|
|
192
|
+
* @default 0
|
|
193
|
+
*/
|
|
194
|
+
dataSourceRevalidateMs: PropTypes.number,
|
|
188
195
|
/**
|
|
189
196
|
* If above 0, the row children will be expanded up to this depth.
|
|
190
197
|
* If equal to -1, all the row children will be expanded.
|
|
@@ -33,6 +33,7 @@ export const DATA_GRID_PRO_PROPS_DEFAULT_VALUES = _extends({}, DATA_GRID_PROPS_D
|
|
|
33
33
|
treeData: false,
|
|
34
34
|
lazyLoading: false,
|
|
35
35
|
lazyLoadingRequestThrottleMs: 500,
|
|
36
|
+
dataSourceRevalidateMs: 0,
|
|
36
37
|
listView: false,
|
|
37
38
|
multipleColumnsSortingMode: 'withModifierKey',
|
|
38
39
|
pinnedColumnsSectionSeparator: 'border-and-shadow',
|
|
@@ -27,7 +27,7 @@ const StyledInputComponent = styled(GridFilterInputValue, {
|
|
|
27
27
|
flex: 1,
|
|
28
28
|
marginRight: vars.spacing(0.5),
|
|
29
29
|
marginBottom: vars.spacing(-0.25),
|
|
30
|
-
'& input[type="
|
|
30
|
+
'& input[type="date"], & input[type="datetime-local"]': {
|
|
31
31
|
'&[value=""]:not(:focus)': {
|
|
32
32
|
color: 'transparent'
|
|
33
33
|
}
|
|
@@ -13,7 +13,7 @@ export declare const useGridDataSourceBasePro: <Api extends GridPrivateApiPro>(a
|
|
|
13
13
|
public: GridDataSourceApiPro;
|
|
14
14
|
private: GridDataSourcePrivateApiPro;
|
|
15
15
|
};
|
|
16
|
-
debouncedFetchRows: ((parentId?:
|
|
16
|
+
debouncedFetchRows: ((parentId?: GridRowId, params?: import("@mui/x-data-grid/internals").GridDataSourceFetchRowsParams<import("@mui/x-data-grid").GridGetRowsParams>) => Promise<void>) & import("@mui/utils/debounce").Cancelable;
|
|
17
17
|
flatTreeStrategyProcessor: {
|
|
18
18
|
strategyName: DataSourceRowsUpdateStrategy;
|
|
19
19
|
group: "dataSourceRowsUpdate";
|
|
@@ -39,6 +39,7 @@ export const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
39
39
|
debouncedFetchRows,
|
|
40
40
|
strategyProcessor: flatTreeStrategyProcessor,
|
|
41
41
|
events,
|
|
42
|
+
startPolling,
|
|
42
43
|
cacheChunkManager,
|
|
43
44
|
cache
|
|
44
45
|
} = useGridDataSourceBase(apiRef, props, _extends({
|
|
@@ -53,6 +54,36 @@ export const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
53
54
|
apiRef.current.setStrategyAvailability(GridStrategyGroup.DataSource, currentStrategy, props.dataSource && !props.lazyLoading ? () => true : () => false);
|
|
54
55
|
}, [apiRef, props.dataSource, props.lazyLoading, props.treeData]);
|
|
55
56
|
const onDataSourceErrorProp = props.onDataSourceError;
|
|
57
|
+
const replaceGroupRows = React.useCallback((groupId, groupPath, rows) => {
|
|
58
|
+
const tree = gridRowTreeSelector(apiRef);
|
|
59
|
+
const fetchedRowIds = new Set(rows.map(row => gridRowIdSelector(apiRef, row)));
|
|
60
|
+
const currentGroupRows = getTreeNodeDescendants(tree, groupId, false, true);
|
|
61
|
+
const rowsToDelete = [];
|
|
62
|
+
currentGroupRows.forEach(rowId => {
|
|
63
|
+
if (fetchedRowIds.has(rowId)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const descendants = getTreeNodeDescendants(tree, rowId, false, false);
|
|
67
|
+
for (let i = descendants.length - 1; i >= 0; i -= 1) {
|
|
68
|
+
const descendantId = descendants[i];
|
|
69
|
+
if (fetchedRowIds.has(descendantId)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
rowsToDelete.push({
|
|
73
|
+
id: descendantId,
|
|
74
|
+
_action: 'delete'
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
rowsToDelete.push({
|
|
78
|
+
id: rowId,
|
|
79
|
+
_action: 'delete'
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
if (rowsToDelete.length > 0) {
|
|
83
|
+
apiRef.current.updateNestedRows(rowsToDelete, groupPath);
|
|
84
|
+
}
|
|
85
|
+
apiRef.current.updateNestedRows(rows, groupPath);
|
|
86
|
+
}, [apiRef]);
|
|
56
87
|
const fetchRowChildren = React.useCallback(async id => {
|
|
57
88
|
const pipedParams = apiRef.current.unstable_applyPipeProcessors('getRowsParams', {});
|
|
58
89
|
if (!props.treeData && (pipedParams.groupFields?.length ?? 0) === 0) {
|
|
@@ -78,7 +109,7 @@ export const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
78
109
|
if (cachedData !== undefined) {
|
|
79
110
|
const rows = cachedData.rows;
|
|
80
111
|
nestedDataManager.setRequestSettled(id);
|
|
81
|
-
|
|
112
|
+
replaceGroupRows(id, rowNode.path, rows);
|
|
82
113
|
if (cachedData.rowCount !== undefined) {
|
|
83
114
|
apiRef.current.setRowCount(cachedData.rowCount);
|
|
84
115
|
}
|
|
@@ -109,23 +140,7 @@ export const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
109
140
|
if (getRowsResponse.rowCount !== undefined) {
|
|
110
141
|
apiRef.current.setRowCount(getRowsResponse.rowCount);
|
|
111
142
|
}
|
|
112
|
-
|
|
113
|
-
const rowsToDelete = [];
|
|
114
|
-
getRowsResponse.rows.forEach(row => {
|
|
115
|
-
const rowId = gridRowIdSelector(apiRef, row);
|
|
116
|
-
const treeNode = gridRowNodeSelector(apiRef, rowId);
|
|
117
|
-
if (treeNode) {
|
|
118
|
-
rowsToDelete.push({
|
|
119
|
-
id: rowId,
|
|
120
|
-
_action: 'delete'
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
if (rowsToDelete.length > 0) {
|
|
125
|
-
// TODO: Make this happen in a single pass by modifying the pre-processing of the rows
|
|
126
|
-
apiRef.current.updateNestedRows(rowsToDelete, rowNode.path);
|
|
127
|
-
}
|
|
128
|
-
apiRef.current.updateNestedRows(getRowsResponse.rows, rowNode.path);
|
|
143
|
+
replaceGroupRows(id, rowNode.path, getRowsResponse.rows);
|
|
129
144
|
apiRef.current.setRowChildrenExpansion(id, true);
|
|
130
145
|
} catch (error) {
|
|
131
146
|
const childrenFetchError = error;
|
|
@@ -143,7 +158,7 @@ export const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
143
158
|
apiRef.current.dataSource.setChildrenLoading(id, false);
|
|
144
159
|
nestedDataManager.setRequestSettled(id);
|
|
145
160
|
}
|
|
146
|
-
}, [nestedDataManager, cacheChunkManager, cache, onDataSourceErrorProp, apiRef, props.treeData, props.dataSource?.getRows]);
|
|
161
|
+
}, [nestedDataManager, cacheChunkManager, cache, onDataSourceErrorProp, replaceGroupRows, apiRef, props.treeData, props.dataSource?.getRows]);
|
|
147
162
|
const setChildrenLoading = React.useCallback((parentId, isLoading) => {
|
|
148
163
|
apiRef.current.setState(state => {
|
|
149
164
|
if (!state.dataSource.loading[parentId] && isLoading === false) {
|
|
@@ -256,7 +271,8 @@ export const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
256
271
|
params: params.fetchParams,
|
|
257
272
|
response
|
|
258
273
|
}, true);
|
|
259
|
-
|
|
274
|
+
startPolling();
|
|
275
|
+
}, [apiRef, startPolling]);
|
|
260
276
|
const dataSourceApi = {
|
|
261
277
|
dataSource: _extends({}, api.public.dataSource, {
|
|
262
278
|
setChildrenLoading,
|
|
@@ -20,7 +20,9 @@ export declare class NestedDataManager {
|
|
|
20
20
|
private maxConcurrentRequests;
|
|
21
21
|
constructor(privateApiRef: RefObject<GridPrivateApiPro>, maxConcurrentRequests?: number);
|
|
22
22
|
private processQueue;
|
|
23
|
-
queue: (ids: GridRowId[]
|
|
23
|
+
queue: (ids: GridRowId[], options?: {
|
|
24
|
+
showChildrenLoading?: boolean;
|
|
25
|
+
}) => Promise<void>;
|
|
24
26
|
setRequestSettled: (id: GridRowId) => void;
|
|
25
27
|
clear: () => void;
|
|
26
28
|
clearPendingRequest: (id: GridRowId) => void;
|
|
@@ -38,17 +38,24 @@ export class NestedDataManager {
|
|
|
38
38
|
this.api.fetchRowChildren(id);
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
|
-
queue = async ids => {
|
|
41
|
+
queue = async (ids, options = {}) => {
|
|
42
|
+
const {
|
|
43
|
+
showChildrenLoading = true
|
|
44
|
+
} = options;
|
|
42
45
|
const loadingIds = {};
|
|
43
46
|
ids.forEach(id => {
|
|
44
47
|
this.queuedRequests.add(id);
|
|
45
|
-
|
|
48
|
+
if (showChildrenLoading) {
|
|
49
|
+
loadingIds[id] = true;
|
|
50
|
+
}
|
|
46
51
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
if (showChildrenLoading) {
|
|
53
|
+
this.api.setState(state => _extends({}, state, {
|
|
54
|
+
dataSource: _extends({}, state.dataSource, {
|
|
55
|
+
loading: _extends({}, state.dataSource.loading, loadingIds)
|
|
56
|
+
})
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
52
59
|
this.processQueue();
|
|
53
60
|
};
|
|
54
61
|
setRequestSettled = id => {
|
|
@@ -6,4 +6,4 @@ import type { DataGridProProcessedProps } from "../../../models/dataGridProProps
|
|
|
6
6
|
* @requires useGridPagination (state)
|
|
7
7
|
* @requires useGridScroll (method
|
|
8
8
|
*/
|
|
9
|
-
export declare const useGridDataSourceLazyLoader: (privateApiRef: RefObject<GridPrivateApiPro>, props: Pick<DataGridProProcessedProps, "dataSource" | "lazyLoading" | "lazyLoadingRequestThrottleMs">) => void;
|
|
9
|
+
export declare const useGridDataSourceLazyLoader: (privateApiRef: RefObject<GridPrivateApiPro>, props: Pick<DataGridProProcessedProps, "dataSource" | "lazyLoading" | "lazyLoadingRequestThrottleMs" | "dataSourceRevalidateMs">) => void;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { throttle } from '@mui/x-internals/throttle';
|
|
6
|
+
import { isDeepEqual } from '@mui/x-internals/isDeepEqual';
|
|
6
7
|
import useEventCallback from '@mui/utils/useEventCallback';
|
|
7
8
|
import debounce from '@mui/utils/debounce';
|
|
8
9
|
import { useGridEvent, gridSortModelSelector, gridFilterModelSelector, GRID_ROOT_GROUP_ID, gridPaginationModelSelector, gridFilteredSortedRowIdsSelector, gridRowIdSelector } from '@mui/x-data-grid';
|
|
@@ -35,10 +36,42 @@ export const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
35
36
|
const loadingTrigger = React.useRef(null);
|
|
36
37
|
const rowsStale = React.useRef(false);
|
|
37
38
|
const draggedRowId = React.useRef(null);
|
|
39
|
+
const pollingIntervalRef = React.useRef(null);
|
|
38
40
|
const fetchRows = React.useCallback(params => {
|
|
39
41
|
privateApiRef.current.dataSource.fetchRows(GRID_ROOT_GROUP_ID, params);
|
|
40
42
|
}, [privateApiRef]);
|
|
41
43
|
const debouncedFetchRows = React.useMemo(() => debounce(fetchRows, 0), [fetchRows]);
|
|
44
|
+
const revalidate = useEventCallback(params => {
|
|
45
|
+
if (rowsStale.current) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check cache first — if data is still cached, skip entirely
|
|
50
|
+
// (no backend call, no diffing needed)
|
|
51
|
+
const cache = privateApiRef.current.dataSource.cache;
|
|
52
|
+
const cachedResponse = cache.get(params);
|
|
53
|
+
if (cachedResponse !== undefined) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Cache is stale/expired — fetch in background (no loading indicator)
|
|
58
|
+
debouncedFetchRows(params);
|
|
59
|
+
});
|
|
60
|
+
const stopPolling = React.useCallback(() => {
|
|
61
|
+
if (pollingIntervalRef.current !== null) {
|
|
62
|
+
clearInterval(pollingIntervalRef.current);
|
|
63
|
+
pollingIntervalRef.current = null;
|
|
64
|
+
}
|
|
65
|
+
}, []);
|
|
66
|
+
const startPolling = useEventCallback(params => {
|
|
67
|
+
stopPolling();
|
|
68
|
+
if (props.dataSourceRevalidateMs <= 0) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
pollingIntervalRef.current = setInterval(() => {
|
|
72
|
+
revalidate(params);
|
|
73
|
+
}, props.dataSourceRevalidateMs);
|
|
74
|
+
});
|
|
42
75
|
const resetGrid = React.useCallback(() => {
|
|
43
76
|
privateApiRef.current.setLoading(true);
|
|
44
77
|
privateApiRef.current.dataSource.cache.clear();
|
|
@@ -89,38 +122,10 @@ export const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
89
122
|
if (rootChildrenCount === 0) {
|
|
90
123
|
return;
|
|
91
124
|
}
|
|
92
|
-
const paginationModel = gridPaginationModelSelector(privateApiRef);
|
|
93
|
-
const pageToSkip = adjustRowParams({
|
|
94
|
-
start: renderedRowsIntervalCache.current.firstRowToRender,
|
|
95
|
-
end: renderedRowsIntervalCache.current.lastRowToRender
|
|
96
|
-
}, {
|
|
97
|
-
pageSize: paginationModel.pageSize,
|
|
98
|
-
rowCount: pageRowCount
|
|
99
|
-
});
|
|
100
125
|
let hasChanged = false;
|
|
101
|
-
const isInitialPage = renderedRowsIntervalCache.current.firstRowToRender === 0 && renderedRowsIntervalCache.current.lastRowToRender === 0;
|
|
102
|
-
for (let i = 0; i < rootChildrenCount; i += 1) {
|
|
103
|
-
if (isInitialPage) {
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
// replace the rows not in the viewport with skeleton rows
|
|
107
|
-
if (pageToSkip.start <= i && i <= pageToSkip.end || tree[rootGroupChildren[i]]?.type === 'skeletonRow' ||
|
|
108
|
-
// ignore rows that are already skeleton rows
|
|
109
|
-
tree[rootGroupChildren[i]]?.id === draggedRowId.current // ignore row that is being dragged (https://github.com/mui/mui-x/issues/17854)
|
|
110
|
-
) {
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
const rowId = tree[rootGroupChildren[i]].id; // keep the id, so that row related state is maintained
|
|
114
|
-
const skeletonRowNode = {
|
|
115
|
-
type: 'skeletonRow',
|
|
116
|
-
id: rowId,
|
|
117
|
-
parent: GRID_ROOT_GROUP_ID,
|
|
118
|
-
depth: 0
|
|
119
|
-
};
|
|
120
|
-
tree[rowId] = skeletonRowNode;
|
|
121
|
-
hasChanged = true;
|
|
122
|
-
}
|
|
123
126
|
|
|
127
|
+
// SWR: Only add skeleton padding for never-fetched positions beyond current data.
|
|
128
|
+
// Previously fetched rows are kept in place (not skeletonized) to avoid flicker on scroll-back.
|
|
124
129
|
// Should only happen with VIEWPORT loading trigger
|
|
125
130
|
if (loadingTrigger.current === LoadingTrigger.VIEWPORT) {
|
|
126
131
|
// fill the grid with skeleton rows
|
|
@@ -186,39 +191,121 @@ export const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
186
191
|
const filteredSortedRowIds = gridFilteredSortedRowIdsSelector(privateApiRef);
|
|
187
192
|
const startingIndex = typeof fetchParams.start === 'string' ? Math.max(filteredSortedRowIds.indexOf(fetchParams.start), 0) : fetchParams.start;
|
|
188
193
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
194
|
+
// Determine if this is a background revalidation (target rows are real, not skeletons)
|
|
195
|
+
const firstTargetRow = rootGroupChildren[startingIndex];
|
|
196
|
+
const isRevalidation = firstTargetRow && tree[firstTargetRow]?.type !== 'skeletonRow';
|
|
197
|
+
if (isRevalidation) {
|
|
198
|
+
// --- SWR PATH ---
|
|
199
|
+
// Compare response row IDs with existing row IDs at target positions
|
|
200
|
+
const newRowIds = response.rows.map(row => gridRowIdSelector(privateApiRef, row));
|
|
201
|
+
const existingRowIds = rootGroupChildren.slice(startingIndex, startingIndex + response.rows.length);
|
|
202
|
+
const sameRowIds = existingRowIds.length === newRowIds.length && existingRowIds.every((id, i) => id === newRowIds[i]);
|
|
203
|
+
if (sameRowIds) {
|
|
204
|
+
// SAME ROW IDs — check for data changes only
|
|
205
|
+
const changedRows = response.rows.filter((newRow, i) => {
|
|
206
|
+
const existingRow = dataRowIdToModelLookup[existingRowIds[i]];
|
|
207
|
+
return !isDeepEqual(newRow, existingRow);
|
|
208
|
+
});
|
|
209
|
+
if (changedRows.length === 0) {
|
|
210
|
+
// No changes — skip update entirely. Cache already refreshed by fetchRows.
|
|
211
|
+
privateApiRef.current.setLoading(false);
|
|
212
|
+
return;
|
|
204
213
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
214
|
+
|
|
215
|
+
// Efficient data-only update — no tree restructuring needed
|
|
216
|
+
privateApiRef.current.updateRows(changedRows);
|
|
217
|
+
// Cache is already updated by fetchRows in useGridDataSourceBase
|
|
218
|
+
} else {
|
|
219
|
+
// DIFFERENT ROW IDs — server returned new rows for this range
|
|
220
|
+
// 1. Remove old rows at target positions
|
|
221
|
+
for (let i = startingIndex; i < startingIndex + response.rows.length && i < rootGroupChildren.length; i += 1) {
|
|
222
|
+
const oldRowId = rootGroupChildren[i];
|
|
223
|
+
if (oldRowId && tree[oldRowId]?.type !== 'skeletonRow') {
|
|
224
|
+
delete tree[oldRowId];
|
|
225
|
+
delete dataRowIdToModelLookup[oldRowId];
|
|
226
|
+
const skeletonId = getSkeletonRowId(i);
|
|
227
|
+
rootGroupChildren[i] = skeletonId;
|
|
228
|
+
tree[skeletonId] = {
|
|
229
|
+
type: 'skeletonRow',
|
|
230
|
+
id: skeletonId,
|
|
231
|
+
parent: GRID_ROOT_GROUP_ID,
|
|
232
|
+
depth: 0
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 2. Duplicate detection for incoming rows
|
|
238
|
+
let duplicateRowCount = 0;
|
|
239
|
+
response.rows.forEach(row => {
|
|
240
|
+
const rowId = gridRowIdSelector(privateApiRef, row);
|
|
241
|
+
if (tree[rowId] || dataRowIdToModelLookup[rowId]) {
|
|
242
|
+
const index = rootGroupChildren.indexOf(rowId);
|
|
243
|
+
if (index !== -1) {
|
|
244
|
+
const skeletonId = getSkeletonRowId(index);
|
|
245
|
+
rootGroupChildren[index] = skeletonId;
|
|
246
|
+
tree[skeletonId] = {
|
|
247
|
+
type: 'skeletonRow',
|
|
248
|
+
id: skeletonId,
|
|
249
|
+
parent: GRID_ROOT_GROUP_ID,
|
|
250
|
+
depth: 0
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
delete tree[rowId];
|
|
254
|
+
delete dataRowIdToModelLookup[rowId];
|
|
255
|
+
duplicateRowCount += 1;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
if (duplicateRowCount > 0) {
|
|
259
|
+
tree[GRID_ROOT_GROUP_ID] = _extends({}, rootGroup, {
|
|
260
|
+
children: rootGroupChildren
|
|
261
|
+
});
|
|
262
|
+
privateApiRef.current.setState(state => _extends({}, state, {
|
|
263
|
+
rows: _extends({}, state.rows, {
|
|
264
|
+
tree,
|
|
265
|
+
dataRowIdToModelLookup
|
|
266
|
+
})
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 3. Replace rows
|
|
271
|
+
privateApiRef.current.unstable_replaceRows(startingIndex, response.rows);
|
|
208
272
|
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
273
|
+
} else {
|
|
274
|
+
// --- ORIGINAL PATH (skeleton → real row replacement) ---
|
|
275
|
+
// Check for duplicate rows
|
|
276
|
+
let duplicateRowCount = 0;
|
|
277
|
+
response.rows.forEach(row => {
|
|
278
|
+
const rowId = gridRowIdSelector(privateApiRef, row);
|
|
279
|
+
if (tree[rowId] || dataRowIdToModelLookup[rowId]) {
|
|
280
|
+
const index = rootGroupChildren.indexOf(rowId);
|
|
281
|
+
if (index !== -1) {
|
|
282
|
+
const skeletonId = getSkeletonRowId(index);
|
|
283
|
+
rootGroupChildren[index] = skeletonId;
|
|
284
|
+
tree[skeletonId] = {
|
|
285
|
+
type: 'skeletonRow',
|
|
286
|
+
id: skeletonId,
|
|
287
|
+
parent: GRID_ROOT_GROUP_ID,
|
|
288
|
+
depth: 0
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
delete tree[rowId];
|
|
292
|
+
delete dataRowIdToModelLookup[rowId];
|
|
293
|
+
duplicateRowCount += 1;
|
|
294
|
+
}
|
|
213
295
|
});
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
219
|
-
|
|
296
|
+
if (duplicateRowCount > 0) {
|
|
297
|
+
tree[GRID_ROOT_GROUP_ID] = _extends({}, rootGroup, {
|
|
298
|
+
children: rootGroupChildren
|
|
299
|
+
});
|
|
300
|
+
privateApiRef.current.setState(state => _extends({}, state, {
|
|
301
|
+
rows: _extends({}, state.rows, {
|
|
302
|
+
tree,
|
|
303
|
+
dataRowIdToModelLookup
|
|
304
|
+
})
|
|
305
|
+
}));
|
|
306
|
+
}
|
|
307
|
+
privateApiRef.current.unstable_replaceRows(startingIndex, response.rows);
|
|
220
308
|
}
|
|
221
|
-
privateApiRef.current.unstable_replaceRows(startingIndex, response.rows);
|
|
222
309
|
}
|
|
223
310
|
rowsStale.current = false;
|
|
224
311
|
if (loadingTrigger.current === null) {
|
|
@@ -230,8 +317,11 @@ export const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
230
317
|
params: params.fetchParams,
|
|
231
318
|
response
|
|
232
319
|
}, false);
|
|
320
|
+
if (loadingTrigger.current === LoadingTrigger.VIEWPORT) {
|
|
321
|
+
startPolling(params.fetchParams);
|
|
322
|
+
}
|
|
233
323
|
privateApiRef.current.requestPipeProcessorsApplication('hydrateRows');
|
|
234
|
-
}, [privateApiRef, updateLoadingTrigger, addSkeletonRows]);
|
|
324
|
+
}, [privateApiRef, updateLoadingTrigger, addSkeletonRows, startPolling]);
|
|
235
325
|
const handleRowCountChange = React.useCallback(() => {
|
|
236
326
|
if (rowsStale.current || loadingTrigger.current === null) {
|
|
237
327
|
return;
|
|
@@ -289,26 +379,46 @@ export const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
289
379
|
visibleRows: currentVisibleRows.rows,
|
|
290
380
|
range: renderContext
|
|
291
381
|
});
|
|
382
|
+
const paginationModel = gridPaginationModelSelector(privateApiRef);
|
|
292
383
|
if (!skeletonRowsSection) {
|
|
384
|
+
// SWR: No skeleton rows in viewport — all visible rows have real data.
|
|
385
|
+
// Schedule background revalidation if cache has expired for this range.
|
|
386
|
+
if (loadingTrigger.current === LoadingTrigger.VIEWPORT) {
|
|
387
|
+
const adjustedParams = adjustRowParams(getRowsParams, {
|
|
388
|
+
pageSize: paginationModel.pageSize,
|
|
389
|
+
rowCount: privateApiRef.current.state.pagination.rowCount
|
|
390
|
+
});
|
|
391
|
+
revalidate(adjustedParams);
|
|
392
|
+
startPolling(adjustedParams);
|
|
393
|
+
}
|
|
293
394
|
return;
|
|
294
395
|
}
|
|
295
396
|
getRowsParams.start = skeletonRowsSection.firstRowIndex;
|
|
296
397
|
getRowsParams.end = skeletonRowsSection.lastRowIndex;
|
|
297
|
-
const paginationModel = gridPaginationModelSelector(privateApiRef);
|
|
298
398
|
fetchRows(adjustRowParams(getRowsParams, {
|
|
299
399
|
pageSize: paginationModel.pageSize,
|
|
300
400
|
rowCount: privateApiRef.current.state.pagination.rowCount
|
|
301
401
|
}));
|
|
302
|
-
}, [privateApiRef, fetchRows]);
|
|
402
|
+
}, [privateApiRef, fetchRows, revalidate, startPolling]);
|
|
303
403
|
const throttledHandleRenderedRowsIntervalChange = React.useMemo(() => throttle(handleRenderedRowsIntervalChange, props.lazyLoadingRequestThrottleMs), [props.lazyLoadingRequestThrottleMs, handleRenderedRowsIntervalChange]);
|
|
304
404
|
React.useEffect(() => {
|
|
305
405
|
return () => {
|
|
306
406
|
throttledHandleRenderedRowsIntervalChange.clear();
|
|
407
|
+
stopPolling();
|
|
307
408
|
};
|
|
308
|
-
}, [throttledHandleRenderedRowsIntervalChange]);
|
|
409
|
+
}, [throttledHandleRenderedRowsIntervalChange, stopPolling]);
|
|
410
|
+
|
|
411
|
+
// Stop polling when dataSourceRevalidateMs is set to 0
|
|
412
|
+
React.useEffect(() => {
|
|
413
|
+
if (props.dataSourceRevalidateMs <= 0) {
|
|
414
|
+
stopPolling();
|
|
415
|
+
}
|
|
416
|
+
}, [props.dataSourceRevalidateMs, stopPolling]);
|
|
417
|
+
React.useEffect(() => stopPolling, [stopPolling]);
|
|
309
418
|
const handleGridSortModelChange = React.useCallback(newSortModel => {
|
|
310
419
|
rowsStale.current = true;
|
|
311
420
|
throttledHandleRenderedRowsIntervalChange.clear();
|
|
421
|
+
stopPolling();
|
|
312
422
|
previousLastRowIndex.current = 0;
|
|
313
423
|
const paginationModel = gridPaginationModelSelector(privateApiRef);
|
|
314
424
|
const filterModel = gridFilterModelSelector(privateApiRef);
|
|
@@ -320,10 +430,11 @@ export const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
320
430
|
};
|
|
321
431
|
privateApiRef.current.setLoading(true);
|
|
322
432
|
debouncedFetchRows(getRowsParams);
|
|
323
|
-
}, [privateApiRef, debouncedFetchRows, throttledHandleRenderedRowsIntervalChange]);
|
|
433
|
+
}, [privateApiRef, debouncedFetchRows, throttledHandleRenderedRowsIntervalChange, stopPolling]);
|
|
324
434
|
const handleGridFilterModelChange = React.useCallback(newFilterModel => {
|
|
325
435
|
rowsStale.current = true;
|
|
326
436
|
throttledHandleRenderedRowsIntervalChange.clear();
|
|
437
|
+
stopPolling();
|
|
327
438
|
previousLastRowIndex.current = 0;
|
|
328
439
|
const paginationModel = gridPaginationModelSelector(privateApiRef);
|
|
329
440
|
const sortModel = gridSortModelSelector(privateApiRef);
|
|
@@ -335,7 +446,7 @@ export const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
335
446
|
};
|
|
336
447
|
privateApiRef.current.setLoading(true);
|
|
337
448
|
debouncedFetchRows(getRowsParams);
|
|
338
|
-
}, [privateApiRef, debouncedFetchRows, throttledHandleRenderedRowsIntervalChange]);
|
|
449
|
+
}, [privateApiRef, debouncedFetchRows, throttledHandleRenderedRowsIntervalChange, stopPolling]);
|
|
339
450
|
const handleDragStart = React.useCallback(row => {
|
|
340
451
|
draggedRowId.current = row.id;
|
|
341
452
|
}, []);
|
package/esm/index.js
CHANGED
|
@@ -13,7 +13,7 @@ export declare const useGridDataSourceBasePro: <Api extends GridPrivateApiPro>(a
|
|
|
13
13
|
public: GridDataSourceApiPro;
|
|
14
14
|
private: GridDataSourcePrivateApiPro;
|
|
15
15
|
};
|
|
16
|
-
debouncedFetchRows: ((parentId?:
|
|
16
|
+
debouncedFetchRows: ((parentId?: GridRowId, params?: import("@mui/x-data-grid/internals").GridDataSourceFetchRowsParams<import("@mui/x-data-grid").GridGetRowsParams>) => Promise<void>) & import("@mui/utils/debounce").Cancelable;
|
|
17
17
|
flatTreeStrategyProcessor: {
|
|
18
18
|
strategyName: DataSourceRowsUpdateStrategy;
|
|
19
19
|
group: "dataSourceRowsUpdate";
|
|
@@ -46,6 +46,7 @@ const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
46
46
|
debouncedFetchRows,
|
|
47
47
|
strategyProcessor: flatTreeStrategyProcessor,
|
|
48
48
|
events,
|
|
49
|
+
startPolling,
|
|
49
50
|
cacheChunkManager,
|
|
50
51
|
cache
|
|
51
52
|
} = (0, _internals.useGridDataSourceBase)(apiRef, props, (0, _extends2.default)({
|
|
@@ -60,6 +61,36 @@ const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
60
61
|
apiRef.current.setStrategyAvailability(_internals.GridStrategyGroup.DataSource, currentStrategy, props.dataSource && !props.lazyLoading ? () => true : () => false);
|
|
61
62
|
}, [apiRef, props.dataSource, props.lazyLoading, props.treeData]);
|
|
62
63
|
const onDataSourceErrorProp = props.onDataSourceError;
|
|
64
|
+
const replaceGroupRows = React.useCallback((groupId, groupPath, rows) => {
|
|
65
|
+
const tree = (0, _xDataGrid.gridRowTreeSelector)(apiRef);
|
|
66
|
+
const fetchedRowIds = new Set(rows.map(row => (0, _xDataGrid.gridRowIdSelector)(apiRef, row)));
|
|
67
|
+
const currentGroupRows = (0, _internals.getTreeNodeDescendants)(tree, groupId, false, true);
|
|
68
|
+
const rowsToDelete = [];
|
|
69
|
+
currentGroupRows.forEach(rowId => {
|
|
70
|
+
if (fetchedRowIds.has(rowId)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const descendants = (0, _internals.getTreeNodeDescendants)(tree, rowId, false, false);
|
|
74
|
+
for (let i = descendants.length - 1; i >= 0; i -= 1) {
|
|
75
|
+
const descendantId = descendants[i];
|
|
76
|
+
if (fetchedRowIds.has(descendantId)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
rowsToDelete.push({
|
|
80
|
+
id: descendantId,
|
|
81
|
+
_action: 'delete'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
rowsToDelete.push({
|
|
85
|
+
id: rowId,
|
|
86
|
+
_action: 'delete'
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
if (rowsToDelete.length > 0) {
|
|
90
|
+
apiRef.current.updateNestedRows(rowsToDelete, groupPath);
|
|
91
|
+
}
|
|
92
|
+
apiRef.current.updateNestedRows(rows, groupPath);
|
|
93
|
+
}, [apiRef]);
|
|
63
94
|
const fetchRowChildren = React.useCallback(async id => {
|
|
64
95
|
const pipedParams = apiRef.current.unstable_applyPipeProcessors('getRowsParams', {});
|
|
65
96
|
if (!props.treeData && (pipedParams.groupFields?.length ?? 0) === 0) {
|
|
@@ -85,7 +116,7 @@ const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
85
116
|
if (cachedData !== undefined) {
|
|
86
117
|
const rows = cachedData.rows;
|
|
87
118
|
nestedDataManager.setRequestSettled(id);
|
|
88
|
-
|
|
119
|
+
replaceGroupRows(id, rowNode.path, rows);
|
|
89
120
|
if (cachedData.rowCount !== undefined) {
|
|
90
121
|
apiRef.current.setRowCount(cachedData.rowCount);
|
|
91
122
|
}
|
|
@@ -116,23 +147,7 @@ const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
116
147
|
if (getRowsResponse.rowCount !== undefined) {
|
|
117
148
|
apiRef.current.setRowCount(getRowsResponse.rowCount);
|
|
118
149
|
}
|
|
119
|
-
|
|
120
|
-
const rowsToDelete = [];
|
|
121
|
-
getRowsResponse.rows.forEach(row => {
|
|
122
|
-
const rowId = (0, _xDataGrid.gridRowIdSelector)(apiRef, row);
|
|
123
|
-
const treeNode = (0, _xDataGrid.gridRowNodeSelector)(apiRef, rowId);
|
|
124
|
-
if (treeNode) {
|
|
125
|
-
rowsToDelete.push({
|
|
126
|
-
id: rowId,
|
|
127
|
-
_action: 'delete'
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
if (rowsToDelete.length > 0) {
|
|
132
|
-
// TODO: Make this happen in a single pass by modifying the pre-processing of the rows
|
|
133
|
-
apiRef.current.updateNestedRows(rowsToDelete, rowNode.path);
|
|
134
|
-
}
|
|
135
|
-
apiRef.current.updateNestedRows(getRowsResponse.rows, rowNode.path);
|
|
150
|
+
replaceGroupRows(id, rowNode.path, getRowsResponse.rows);
|
|
136
151
|
apiRef.current.setRowChildrenExpansion(id, true);
|
|
137
152
|
} catch (error) {
|
|
138
153
|
const childrenFetchError = error;
|
|
@@ -150,7 +165,7 @@ const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
150
165
|
apiRef.current.dataSource.setChildrenLoading(id, false);
|
|
151
166
|
nestedDataManager.setRequestSettled(id);
|
|
152
167
|
}
|
|
153
|
-
}, [nestedDataManager, cacheChunkManager, cache, onDataSourceErrorProp, apiRef, props.treeData, props.dataSource?.getRows]);
|
|
168
|
+
}, [nestedDataManager, cacheChunkManager, cache, onDataSourceErrorProp, replaceGroupRows, apiRef, props.treeData, props.dataSource?.getRows]);
|
|
154
169
|
const setChildrenLoading = React.useCallback((parentId, isLoading) => {
|
|
155
170
|
apiRef.current.setState(state => {
|
|
156
171
|
if (!state.dataSource.loading[parentId] && isLoading === false) {
|
|
@@ -263,7 +278,8 @@ const useGridDataSourceBasePro = (apiRef, props, options = {}) => {
|
|
|
263
278
|
params: params.fetchParams,
|
|
264
279
|
response
|
|
265
280
|
}, true);
|
|
266
|
-
|
|
281
|
+
startPolling();
|
|
282
|
+
}, [apiRef, startPolling]);
|
|
267
283
|
const dataSourceApi = {
|
|
268
284
|
dataSource: (0, _extends2.default)({}, api.public.dataSource, {
|
|
269
285
|
setChildrenLoading,
|
|
@@ -20,7 +20,9 @@ export declare class NestedDataManager {
|
|
|
20
20
|
private maxConcurrentRequests;
|
|
21
21
|
constructor(privateApiRef: RefObject<GridPrivateApiPro>, maxConcurrentRequests?: number);
|
|
22
22
|
private processQueue;
|
|
23
|
-
queue: (ids: GridRowId[]
|
|
23
|
+
queue: (ids: GridRowId[], options?: {
|
|
24
|
+
showChildrenLoading?: boolean;
|
|
25
|
+
}) => Promise<void>;
|
|
24
26
|
setRequestSettled: (id: GridRowId) => void;
|
|
25
27
|
clear: () => void;
|
|
26
28
|
clearPendingRequest: (id: GridRowId) => void;
|
|
@@ -44,17 +44,24 @@ class NestedDataManager {
|
|
|
44
44
|
this.api.fetchRowChildren(id);
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
-
queue = async ids => {
|
|
47
|
+
queue = async (ids, options = {}) => {
|
|
48
|
+
const {
|
|
49
|
+
showChildrenLoading = true
|
|
50
|
+
} = options;
|
|
48
51
|
const loadingIds = {};
|
|
49
52
|
ids.forEach(id => {
|
|
50
53
|
this.queuedRequests.add(id);
|
|
51
|
-
|
|
54
|
+
if (showChildrenLoading) {
|
|
55
|
+
loadingIds[id] = true;
|
|
56
|
+
}
|
|
52
57
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
if (showChildrenLoading) {
|
|
59
|
+
this.api.setState(state => (0, _extends2.default)({}, state, {
|
|
60
|
+
dataSource: (0, _extends2.default)({}, state.dataSource, {
|
|
61
|
+
loading: (0, _extends2.default)({}, state.dataSource.loading, loadingIds)
|
|
62
|
+
})
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
58
65
|
this.processQueue();
|
|
59
66
|
};
|
|
60
67
|
setRequestSettled = id => {
|
|
@@ -6,4 +6,4 @@ import type { DataGridProProcessedProps } from "../../../models/dataGridProProps
|
|
|
6
6
|
* @requires useGridPagination (state)
|
|
7
7
|
* @requires useGridScroll (method
|
|
8
8
|
*/
|
|
9
|
-
export declare const useGridDataSourceLazyLoader: (privateApiRef: RefObject<GridPrivateApiPro>, props: Pick<DataGridProProcessedProps, "dataSource" | "lazyLoading" | "lazyLoadingRequestThrottleMs">) => void;
|
|
9
|
+
export declare const useGridDataSourceLazyLoader: (privateApiRef: RefObject<GridPrivateApiPro>, props: Pick<DataGridProProcessedProps, "dataSource" | "lazyLoading" | "lazyLoadingRequestThrottleMs" | "dataSourceRevalidateMs">) => void;
|
|
@@ -10,6 +10,7 @@ exports.useGridDataSourceLazyLoader = void 0;
|
|
|
10
10
|
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
|
11
11
|
var React = _interopRequireWildcard(require("react"));
|
|
12
12
|
var _throttle = require("@mui/x-internals/throttle");
|
|
13
|
+
var _isDeepEqual = require("@mui/x-internals/isDeepEqual");
|
|
13
14
|
var _useEventCallback = _interopRequireDefault(require("@mui/utils/useEventCallback"));
|
|
14
15
|
var _debounce = _interopRequireDefault(require("@mui/utils/debounce"));
|
|
15
16
|
var _xDataGrid = require("@mui/x-data-grid");
|
|
@@ -42,10 +43,42 @@ const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
42
43
|
const loadingTrigger = React.useRef(null);
|
|
43
44
|
const rowsStale = React.useRef(false);
|
|
44
45
|
const draggedRowId = React.useRef(null);
|
|
46
|
+
const pollingIntervalRef = React.useRef(null);
|
|
45
47
|
const fetchRows = React.useCallback(params => {
|
|
46
48
|
privateApiRef.current.dataSource.fetchRows(_xDataGrid.GRID_ROOT_GROUP_ID, params);
|
|
47
49
|
}, [privateApiRef]);
|
|
48
50
|
const debouncedFetchRows = React.useMemo(() => (0, _debounce.default)(fetchRows, 0), [fetchRows]);
|
|
51
|
+
const revalidate = (0, _useEventCallback.default)(params => {
|
|
52
|
+
if (rowsStale.current) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check cache first — if data is still cached, skip entirely
|
|
57
|
+
// (no backend call, no diffing needed)
|
|
58
|
+
const cache = privateApiRef.current.dataSource.cache;
|
|
59
|
+
const cachedResponse = cache.get(params);
|
|
60
|
+
if (cachedResponse !== undefined) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Cache is stale/expired — fetch in background (no loading indicator)
|
|
65
|
+
debouncedFetchRows(params);
|
|
66
|
+
});
|
|
67
|
+
const stopPolling = React.useCallback(() => {
|
|
68
|
+
if (pollingIntervalRef.current !== null) {
|
|
69
|
+
clearInterval(pollingIntervalRef.current);
|
|
70
|
+
pollingIntervalRef.current = null;
|
|
71
|
+
}
|
|
72
|
+
}, []);
|
|
73
|
+
const startPolling = (0, _useEventCallback.default)(params => {
|
|
74
|
+
stopPolling();
|
|
75
|
+
if (props.dataSourceRevalidateMs <= 0) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
pollingIntervalRef.current = setInterval(() => {
|
|
79
|
+
revalidate(params);
|
|
80
|
+
}, props.dataSourceRevalidateMs);
|
|
81
|
+
});
|
|
49
82
|
const resetGrid = React.useCallback(() => {
|
|
50
83
|
privateApiRef.current.setLoading(true);
|
|
51
84
|
privateApiRef.current.dataSource.cache.clear();
|
|
@@ -96,38 +129,10 @@ const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
96
129
|
if (rootChildrenCount === 0) {
|
|
97
130
|
return;
|
|
98
131
|
}
|
|
99
|
-
const paginationModel = (0, _xDataGrid.gridPaginationModelSelector)(privateApiRef);
|
|
100
|
-
const pageToSkip = (0, _utils.adjustRowParams)({
|
|
101
|
-
start: renderedRowsIntervalCache.current.firstRowToRender,
|
|
102
|
-
end: renderedRowsIntervalCache.current.lastRowToRender
|
|
103
|
-
}, {
|
|
104
|
-
pageSize: paginationModel.pageSize,
|
|
105
|
-
rowCount: pageRowCount
|
|
106
|
-
});
|
|
107
132
|
let hasChanged = false;
|
|
108
|
-
const isInitialPage = renderedRowsIntervalCache.current.firstRowToRender === 0 && renderedRowsIntervalCache.current.lastRowToRender === 0;
|
|
109
|
-
for (let i = 0; i < rootChildrenCount; i += 1) {
|
|
110
|
-
if (isInitialPage) {
|
|
111
|
-
break;
|
|
112
|
-
}
|
|
113
|
-
// replace the rows not in the viewport with skeleton rows
|
|
114
|
-
if (pageToSkip.start <= i && i <= pageToSkip.end || tree[rootGroupChildren[i]]?.type === 'skeletonRow' ||
|
|
115
|
-
// ignore rows that are already skeleton rows
|
|
116
|
-
tree[rootGroupChildren[i]]?.id === draggedRowId.current // ignore row that is being dragged (https://github.com/mui/mui-x/issues/17854)
|
|
117
|
-
) {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
const rowId = tree[rootGroupChildren[i]].id; // keep the id, so that row related state is maintained
|
|
121
|
-
const skeletonRowNode = {
|
|
122
|
-
type: 'skeletonRow',
|
|
123
|
-
id: rowId,
|
|
124
|
-
parent: _xDataGrid.GRID_ROOT_GROUP_ID,
|
|
125
|
-
depth: 0
|
|
126
|
-
};
|
|
127
|
-
tree[rowId] = skeletonRowNode;
|
|
128
|
-
hasChanged = true;
|
|
129
|
-
}
|
|
130
133
|
|
|
134
|
+
// SWR: Only add skeleton padding for never-fetched positions beyond current data.
|
|
135
|
+
// Previously fetched rows are kept in place (not skeletonized) to avoid flicker on scroll-back.
|
|
131
136
|
// Should only happen with VIEWPORT loading trigger
|
|
132
137
|
if (loadingTrigger.current === LoadingTrigger.VIEWPORT) {
|
|
133
138
|
// fill the grid with skeleton rows
|
|
@@ -193,39 +198,121 @@ const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
193
198
|
const filteredSortedRowIds = (0, _xDataGrid.gridFilteredSortedRowIdsSelector)(privateApiRef);
|
|
194
199
|
const startingIndex = typeof fetchParams.start === 'string' ? Math.max(filteredSortedRowIds.indexOf(fetchParams.start), 0) : fetchParams.start;
|
|
195
200
|
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
201
|
+
// Determine if this is a background revalidation (target rows are real, not skeletons)
|
|
202
|
+
const firstTargetRow = rootGroupChildren[startingIndex];
|
|
203
|
+
const isRevalidation = firstTargetRow && tree[firstTargetRow]?.type !== 'skeletonRow';
|
|
204
|
+
if (isRevalidation) {
|
|
205
|
+
// --- SWR PATH ---
|
|
206
|
+
// Compare response row IDs with existing row IDs at target positions
|
|
207
|
+
const newRowIds = response.rows.map(row => (0, _xDataGrid.gridRowIdSelector)(privateApiRef, row));
|
|
208
|
+
const existingRowIds = rootGroupChildren.slice(startingIndex, startingIndex + response.rows.length);
|
|
209
|
+
const sameRowIds = existingRowIds.length === newRowIds.length && existingRowIds.every((id, i) => id === newRowIds[i]);
|
|
210
|
+
if (sameRowIds) {
|
|
211
|
+
// SAME ROW IDs — check for data changes only
|
|
212
|
+
const changedRows = response.rows.filter((newRow, i) => {
|
|
213
|
+
const existingRow = dataRowIdToModelLookup[existingRowIds[i]];
|
|
214
|
+
return !(0, _isDeepEqual.isDeepEqual)(newRow, existingRow);
|
|
215
|
+
});
|
|
216
|
+
if (changedRows.length === 0) {
|
|
217
|
+
// No changes — skip update entirely. Cache already refreshed by fetchRows.
|
|
218
|
+
privateApiRef.current.setLoading(false);
|
|
219
|
+
return;
|
|
211
220
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
221
|
+
|
|
222
|
+
// Efficient data-only update — no tree restructuring needed
|
|
223
|
+
privateApiRef.current.updateRows(changedRows);
|
|
224
|
+
// Cache is already updated by fetchRows in useGridDataSourceBase
|
|
225
|
+
} else {
|
|
226
|
+
// DIFFERENT ROW IDs — server returned new rows for this range
|
|
227
|
+
// 1. Remove old rows at target positions
|
|
228
|
+
for (let i = startingIndex; i < startingIndex + response.rows.length && i < rootGroupChildren.length; i += 1) {
|
|
229
|
+
const oldRowId = rootGroupChildren[i];
|
|
230
|
+
if (oldRowId && tree[oldRowId]?.type !== 'skeletonRow') {
|
|
231
|
+
delete tree[oldRowId];
|
|
232
|
+
delete dataRowIdToModelLookup[oldRowId];
|
|
233
|
+
const skeletonId = getSkeletonRowId(i);
|
|
234
|
+
rootGroupChildren[i] = skeletonId;
|
|
235
|
+
tree[skeletonId] = {
|
|
236
|
+
type: 'skeletonRow',
|
|
237
|
+
id: skeletonId,
|
|
238
|
+
parent: _xDataGrid.GRID_ROOT_GROUP_ID,
|
|
239
|
+
depth: 0
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 2. Duplicate detection for incoming rows
|
|
245
|
+
let duplicateRowCount = 0;
|
|
246
|
+
response.rows.forEach(row => {
|
|
247
|
+
const rowId = (0, _xDataGrid.gridRowIdSelector)(privateApiRef, row);
|
|
248
|
+
if (tree[rowId] || dataRowIdToModelLookup[rowId]) {
|
|
249
|
+
const index = rootGroupChildren.indexOf(rowId);
|
|
250
|
+
if (index !== -1) {
|
|
251
|
+
const skeletonId = getSkeletonRowId(index);
|
|
252
|
+
rootGroupChildren[index] = skeletonId;
|
|
253
|
+
tree[skeletonId] = {
|
|
254
|
+
type: 'skeletonRow',
|
|
255
|
+
id: skeletonId,
|
|
256
|
+
parent: _xDataGrid.GRID_ROOT_GROUP_ID,
|
|
257
|
+
depth: 0
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
delete tree[rowId];
|
|
261
|
+
delete dataRowIdToModelLookup[rowId];
|
|
262
|
+
duplicateRowCount += 1;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
if (duplicateRowCount > 0) {
|
|
266
|
+
tree[_xDataGrid.GRID_ROOT_GROUP_ID] = (0, _extends2.default)({}, rootGroup, {
|
|
267
|
+
children: rootGroupChildren
|
|
268
|
+
});
|
|
269
|
+
privateApiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
270
|
+
rows: (0, _extends2.default)({}, state.rows, {
|
|
271
|
+
tree,
|
|
272
|
+
dataRowIdToModelLookup
|
|
273
|
+
})
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 3. Replace rows
|
|
278
|
+
privateApiRef.current.unstable_replaceRows(startingIndex, response.rows);
|
|
215
279
|
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
280
|
+
} else {
|
|
281
|
+
// --- ORIGINAL PATH (skeleton → real row replacement) ---
|
|
282
|
+
// Check for duplicate rows
|
|
283
|
+
let duplicateRowCount = 0;
|
|
284
|
+
response.rows.forEach(row => {
|
|
285
|
+
const rowId = (0, _xDataGrid.gridRowIdSelector)(privateApiRef, row);
|
|
286
|
+
if (tree[rowId] || dataRowIdToModelLookup[rowId]) {
|
|
287
|
+
const index = rootGroupChildren.indexOf(rowId);
|
|
288
|
+
if (index !== -1) {
|
|
289
|
+
const skeletonId = getSkeletonRowId(index);
|
|
290
|
+
rootGroupChildren[index] = skeletonId;
|
|
291
|
+
tree[skeletonId] = {
|
|
292
|
+
type: 'skeletonRow',
|
|
293
|
+
id: skeletonId,
|
|
294
|
+
parent: _xDataGrid.GRID_ROOT_GROUP_ID,
|
|
295
|
+
depth: 0
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
delete tree[rowId];
|
|
299
|
+
delete dataRowIdToModelLookup[rowId];
|
|
300
|
+
duplicateRowCount += 1;
|
|
301
|
+
}
|
|
220
302
|
});
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
|
|
303
|
+
if (duplicateRowCount > 0) {
|
|
304
|
+
tree[_xDataGrid.GRID_ROOT_GROUP_ID] = (0, _extends2.default)({}, rootGroup, {
|
|
305
|
+
children: rootGroupChildren
|
|
306
|
+
});
|
|
307
|
+
privateApiRef.current.setState(state => (0, _extends2.default)({}, state, {
|
|
308
|
+
rows: (0, _extends2.default)({}, state.rows, {
|
|
309
|
+
tree,
|
|
310
|
+
dataRowIdToModelLookup
|
|
311
|
+
})
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
privateApiRef.current.unstable_replaceRows(startingIndex, response.rows);
|
|
227
315
|
}
|
|
228
|
-
privateApiRef.current.unstable_replaceRows(startingIndex, response.rows);
|
|
229
316
|
}
|
|
230
317
|
rowsStale.current = false;
|
|
231
318
|
if (loadingTrigger.current === null) {
|
|
@@ -237,8 +324,11 @@ const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
237
324
|
params: params.fetchParams,
|
|
238
325
|
response
|
|
239
326
|
}, false);
|
|
327
|
+
if (loadingTrigger.current === LoadingTrigger.VIEWPORT) {
|
|
328
|
+
startPolling(params.fetchParams);
|
|
329
|
+
}
|
|
240
330
|
privateApiRef.current.requestPipeProcessorsApplication('hydrateRows');
|
|
241
|
-
}, [privateApiRef, updateLoadingTrigger, addSkeletonRows]);
|
|
331
|
+
}, [privateApiRef, updateLoadingTrigger, addSkeletonRows, startPolling]);
|
|
242
332
|
const handleRowCountChange = React.useCallback(() => {
|
|
243
333
|
if (rowsStale.current || loadingTrigger.current === null) {
|
|
244
334
|
return;
|
|
@@ -296,26 +386,46 @@ const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
296
386
|
visibleRows: currentVisibleRows.rows,
|
|
297
387
|
range: renderContext
|
|
298
388
|
});
|
|
389
|
+
const paginationModel = (0, _xDataGrid.gridPaginationModelSelector)(privateApiRef);
|
|
299
390
|
if (!skeletonRowsSection) {
|
|
391
|
+
// SWR: No skeleton rows in viewport — all visible rows have real data.
|
|
392
|
+
// Schedule background revalidation if cache has expired for this range.
|
|
393
|
+
if (loadingTrigger.current === LoadingTrigger.VIEWPORT) {
|
|
394
|
+
const adjustedParams = (0, _utils.adjustRowParams)(getRowsParams, {
|
|
395
|
+
pageSize: paginationModel.pageSize,
|
|
396
|
+
rowCount: privateApiRef.current.state.pagination.rowCount
|
|
397
|
+
});
|
|
398
|
+
revalidate(adjustedParams);
|
|
399
|
+
startPolling(adjustedParams);
|
|
400
|
+
}
|
|
300
401
|
return;
|
|
301
402
|
}
|
|
302
403
|
getRowsParams.start = skeletonRowsSection.firstRowIndex;
|
|
303
404
|
getRowsParams.end = skeletonRowsSection.lastRowIndex;
|
|
304
|
-
const paginationModel = (0, _xDataGrid.gridPaginationModelSelector)(privateApiRef);
|
|
305
405
|
fetchRows((0, _utils.adjustRowParams)(getRowsParams, {
|
|
306
406
|
pageSize: paginationModel.pageSize,
|
|
307
407
|
rowCount: privateApiRef.current.state.pagination.rowCount
|
|
308
408
|
}));
|
|
309
|
-
}, [privateApiRef, fetchRows]);
|
|
409
|
+
}, [privateApiRef, fetchRows, revalidate, startPolling]);
|
|
310
410
|
const throttledHandleRenderedRowsIntervalChange = React.useMemo(() => (0, _throttle.throttle)(handleRenderedRowsIntervalChange, props.lazyLoadingRequestThrottleMs), [props.lazyLoadingRequestThrottleMs, handleRenderedRowsIntervalChange]);
|
|
311
411
|
React.useEffect(() => {
|
|
312
412
|
return () => {
|
|
313
413
|
throttledHandleRenderedRowsIntervalChange.clear();
|
|
414
|
+
stopPolling();
|
|
314
415
|
};
|
|
315
|
-
}, [throttledHandleRenderedRowsIntervalChange]);
|
|
416
|
+
}, [throttledHandleRenderedRowsIntervalChange, stopPolling]);
|
|
417
|
+
|
|
418
|
+
// Stop polling when dataSourceRevalidateMs is set to 0
|
|
419
|
+
React.useEffect(() => {
|
|
420
|
+
if (props.dataSourceRevalidateMs <= 0) {
|
|
421
|
+
stopPolling();
|
|
422
|
+
}
|
|
423
|
+
}, [props.dataSourceRevalidateMs, stopPolling]);
|
|
424
|
+
React.useEffect(() => stopPolling, [stopPolling]);
|
|
316
425
|
const handleGridSortModelChange = React.useCallback(newSortModel => {
|
|
317
426
|
rowsStale.current = true;
|
|
318
427
|
throttledHandleRenderedRowsIntervalChange.clear();
|
|
428
|
+
stopPolling();
|
|
319
429
|
previousLastRowIndex.current = 0;
|
|
320
430
|
const paginationModel = (0, _xDataGrid.gridPaginationModelSelector)(privateApiRef);
|
|
321
431
|
const filterModel = (0, _xDataGrid.gridFilterModelSelector)(privateApiRef);
|
|
@@ -327,10 +437,11 @@ const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
327
437
|
};
|
|
328
438
|
privateApiRef.current.setLoading(true);
|
|
329
439
|
debouncedFetchRows(getRowsParams);
|
|
330
|
-
}, [privateApiRef, debouncedFetchRows, throttledHandleRenderedRowsIntervalChange]);
|
|
440
|
+
}, [privateApiRef, debouncedFetchRows, throttledHandleRenderedRowsIntervalChange, stopPolling]);
|
|
331
441
|
const handleGridFilterModelChange = React.useCallback(newFilterModel => {
|
|
332
442
|
rowsStale.current = true;
|
|
333
443
|
throttledHandleRenderedRowsIntervalChange.clear();
|
|
444
|
+
stopPolling();
|
|
334
445
|
previousLastRowIndex.current = 0;
|
|
335
446
|
const paginationModel = (0, _xDataGrid.gridPaginationModelSelector)(privateApiRef);
|
|
336
447
|
const sortModel = (0, _xDataGrid.gridSortModelSelector)(privateApiRef);
|
|
@@ -342,7 +453,7 @@ const useGridDataSourceLazyLoader = (privateApiRef, props) => {
|
|
|
342
453
|
};
|
|
343
454
|
privateApiRef.current.setLoading(true);
|
|
344
455
|
debouncedFetchRows(getRowsParams);
|
|
345
|
-
}, [privateApiRef, debouncedFetchRows, throttledHandleRenderedRowsIntervalChange]);
|
|
456
|
+
}, [privateApiRef, debouncedFetchRows, throttledHandleRenderedRowsIntervalChange, stopPolling]);
|
|
346
457
|
const handleDragStart = React.useCallback(row => {
|
|
347
458
|
draggedRowId.current = row.id;
|
|
348
459
|
}, []);
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mui/x-data-grid-pro",
|
|
3
|
-
"version": "8.27.
|
|
3
|
+
"version": "8.27.3",
|
|
4
4
|
"author": "MUI Team",
|
|
5
5
|
"description": "The Pro plan edition of the MUI X Data Grid components.",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@mui/utils": "^7.3.5",
|
|
38
38
|
"clsx": "^2.1.1",
|
|
39
39
|
"prop-types": "^15.8.1",
|
|
40
|
-
"@mui/x-data-grid": "8.27.
|
|
40
|
+
"@mui/x-data-grid": "8.27.3",
|
|
41
41
|
"@mui/x-internals": "8.26.0",
|
|
42
42
|
"@mui/x-license": "8.26.0"
|
|
43
43
|
},
|