Skip to content

Commit 19435ad

Browse files
[DataGrid] Reset sortedRows state on prop change (#599)
* Fix #575 reset sortedRows state on prop change * fix typescript issue * fix lint * prettier * update dep array * polish tests * Update packages/storybook/src/stories/grid-sorting.stories.tsx Co-authored-by: Olivier Tassinari <[email protected]> * Update packages/storybook/src/stories/grid-sorting.stories.tsx Co-authored-by: Olivier Tassinari <[email protected]> * avoids confusion, sort is not immutable * cleanup story * refactor applySorting * fix lint Co-authored-by: Olivier Tassinari <[email protected]>
1 parent 0aa3491 commit 19435ad

File tree

5 files changed

+111
-34
lines changed

5 files changed

+111
-34
lines changed

packages/grid/_modules_/grid/GridComponent.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export const GridComponent = React.forwardRef<HTMLDivElement, GridComponentProps
7373
useRows(props.rows, apiRef);
7474
useKeyboard(rootContainerRef, apiRef);
7575
useSelection(apiRef);
76-
useSorting(apiRef);
76+
useSorting(apiRef, props.rows);
7777

7878
useContainerProps(windowRef, apiRef);
7979
const renderCtx = useVirtualRows(columnsHeaderRef, windowRef, renderingZoneRef, apiRef);

packages/grid/_modules_/grid/hooks/features/sorting/useSorting.ts

+14-16
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { FeatureModeConstant } from '../../../models/featureMode';
1313
import { CellParams } from '../../../models/params/cellParams';
1414
import { ColParams } from '../../../models/params/colParams';
1515
import { SortModelParams } from '../../../models/params/sortModelParams';
16-
import { RowModel } from '../../../models/rows';
16+
import { RowModel, RowsProp } from '../../../models/rows';
1717
import { FieldComparatorList, SortItem, SortModel } from '../../../models/sortModel';
1818
import { buildCellParams } from '../../../utils/paramsUtils';
1919
import { isDesc, nextSortDirection } from '../../../utils/sortingUtils';
@@ -26,10 +26,10 @@ import { columnsSelector } from '../columns/columnsSelector';
2626
import { GridState } from '../core/gridState';
2727
import { useGridSelector } from '../core/useGridSelector';
2828
import { useGridState } from '../core/useGridState';
29-
import { rowCountSelector, unorderedRowModelsSelector } from '../rows/rowsSelector';
29+
import { rowCountSelector } from '../rows/rowsSelector';
3030
import { SortingState } from './sortingState';
3131

32-
export const useSorting = (apiRef: ApiRef) => {
32+
export const useSorting = (apiRef: ApiRef, rowsProp: RowsProp) => {
3333
const logger = useLogger('useSorting');
3434
const allowMultipleSorting = React.useRef<boolean>(false);
3535
const comparatorList = React.useRef<FieldComparatorList>([]);
@@ -38,7 +38,6 @@ export const useSorting = (apiRef: ApiRef) => {
3838
const options = useGridSelector(apiRef, optionsSelector);
3939
const columns = useGridSelector(apiRef, columnsSelector);
4040
const rowCount = useGridSelector(apiRef, rowCountSelector);
41-
const unorderedRows = useGridSelector(apiRef, unorderedRowModelsSelector);
4241

4342
const getSortModelParams = React.useCallback(
4443
(sortModel: SortModel): SortModelParams => ({
@@ -127,13 +126,14 @@ export const useSorting = (apiRef: ApiRef) => {
127126
);
128127

129128
const applySorting = React.useCallback(() => {
129+
const rowModels = apiRef.current.getRowModels();
130130
const sortModel = apiRef.current.getState<GridState>().sorting.sortModel;
131131
logger.info('Sorting rows with ', sortModel);
132132

133-
let sorted = [...unorderedRows];
133+
const sorted = [...rowModels];
134134
if (sortModel.length > 0) {
135135
comparatorList.current = buildComparatorList(sortModel);
136-
sorted = sorted.sort(comparatorListAggregate);
136+
sorted.sort(comparatorListAggregate);
137137
}
138138

139139
setGridState((oldState) => {
@@ -143,15 +143,7 @@ export const useSorting = (apiRef: ApiRef) => {
143143
};
144144
});
145145
forceUpdate();
146-
}, [
147-
apiRef,
148-
logger,
149-
unorderedRows,
150-
setGridState,
151-
forceUpdate,
152-
buildComparatorList,
153-
comparatorListAggregate,
154-
]);
146+
}, [apiRef, logger, setGridState, forceUpdate, buildComparatorList, comparatorListAggregate]);
155147

156148
const setSortModel = React.useCallback(
157149
(sortModel: SortModel) => {
@@ -236,6 +228,11 @@ export const useSorting = (apiRef: ApiRef) => {
236228
const sortApi: SortApi = { getSortModel, setSortModel, onSortModelChange, applySorting };
237229
useApiMethod(apiRef, sortApi, 'SortApi');
238230

231+
React.useEffect(() => {
232+
// When the rows prop change, we re apply the sorting.
233+
apiRef.current.applySorting();
234+
}, [apiRef, rowsProp]);
235+
239236
React.useEffect(() => {
240237
if (rowCount > 0 && options.sortingMode === FeatureModeConstant.client) {
241238
logger.debug('row changed, applying sortModel');
@@ -268,7 +265,8 @@ export const useSorting = (apiRef: ApiRef) => {
268265

269266
React.useEffect(() => {
270267
const sortModel = options.sortModel || [];
271-
if (sortModel.length > 0) {
268+
const oldSortModel = apiRef.current.state.sorting.sortModel;
269+
if (sortModel.length > 0 && !isEqual(sortModel, oldSortModel)) {
272270
// we use apiRef to avoid watching setSortModel as it will trigger an update on every state change
273271
apiRef.current.setSortModel(sortModel);
274272
}

packages/grid/data-grid/src/DataGrid.test.tsx

+61-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import PropTypes from 'prop-types';
33
// @ts-expect-error need to migrate helpers to TypeScript
4-
import { createClientRender, ErrorBoundary } from 'test/utils';
4+
import { createClientRender, fireEvent, screen, ErrorBoundary } from 'test/utils';
55
import { useFakeTimers } from 'sinon';
66
import { expect } from 'chai';
77
import { DataGrid } from '@material-ui/data-grid';
@@ -266,6 +266,66 @@ describe('<DataGrid />', () => {
266266
expect(getColumnValues()).to.deep.equal(['Puma', 'Nike', 'Adidas'].reverse());
267267
});
268268
});
269+
270+
describe('sorting', () => {
271+
it('should sort when clicking the header cell', () => {
272+
render(
273+
<div style={{ width: 300, height: 300 }}>
274+
<DataGrid {...defaultProps} />
275+
</div>,
276+
);
277+
const header = screen.getByRole('columnheader', { name: 'brand' });
278+
expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']);
279+
fireEvent.click(header);
280+
expect(getColumnValues()).to.deep.equal(['Adidas', 'Nike', 'Puma']);
281+
fireEvent.click(header);
282+
expect(getColumnValues()).to.deep.equal(['Puma', 'Nike', 'Adidas']);
283+
});
284+
285+
it('should keep rows sorted when rows prop change', () => {
286+
interface TestCaseProps {
287+
rows: any[];
288+
}
289+
const TestCase = (props: TestCaseProps) => {
290+
const { rows } = props;
291+
return (
292+
<div style={{ width: 300, height: 300 }}>
293+
<DataGrid
294+
{...defaultProps}
295+
rows={rows}
296+
sortModel={[
297+
{
298+
field: 'brand',
299+
sort: 'asc',
300+
},
301+
]}
302+
/>
303+
</div>
304+
);
305+
};
306+
307+
const { setProps } = render(<TestCase rows={defaultProps.rows} />);
308+
expect(getColumnValues()).to.deep.equal(['Adidas', 'Nike', 'Puma']);
309+
310+
setProps({
311+
rows: [
312+
{
313+
id: 3,
314+
brand: 'Asics',
315+
},
316+
{
317+
id: 4,
318+
brand: 'RedBull',
319+
},
320+
{
321+
id: 5,
322+
brand: 'Hugo',
323+
},
324+
],
325+
});
326+
expect(getColumnValues()).to.deep.equal(['Asics', 'Hugo', 'RedBull']);
327+
});
328+
});
269329
});
270330

271331
describe('warnings', () => {

packages/grid/x-grid/src/XGrid.test.tsx

-16
Original file line numberDiff line numberDiff line change
@@ -206,22 +206,6 @@ describe('<XGrid />', () => {
206206
});
207207
});
208208

209-
describe('sorting', () => {
210-
it('should sort when clicking the header cell', () => {
211-
render(
212-
<div style={{ width: 300, height: 300 }}>
213-
<XGrid {...defaultProps} />
214-
</div>,
215-
);
216-
const header = screen.getByRole('columnheader', { name: 'brand' });
217-
expect(getColumnValues()).to.deep.equal(['Nike', 'Adidas', 'Puma']);
218-
fireEvent.click(header);
219-
expect(getColumnValues()).to.deep.equal(['Adidas', 'Nike', 'Puma']);
220-
fireEvent.click(header);
221-
expect(getColumnValues()).to.deep.equal(['Puma', 'Nike', 'Adidas']);
222-
});
223-
});
224-
225209
describe('state', () => {
226210
it('should trigger on state change and pass the correct params', () => {
227211
let onStateParams;

packages/storybook/src/stories/grid-sorting.stories.tsx

+35
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Button } from '@material-ui/core';
2+
import { randomInt } from '@material-ui/x-grid-data-generator';
13
import * as React from 'react';
24
import {
35
ColDef,
@@ -432,3 +434,36 @@ export const ServerSideSorting = () => {
432434
</div>
433435
);
434436
};
437+
438+
export const ResetSortingRows = () => {
439+
const columns = [
440+
{
441+
field: 'name',
442+
width: 200,
443+
},
444+
{
445+
field: 'team',
446+
width: 200,
447+
type: 'number',
448+
},
449+
];
450+
const [rows, setRows] = React.useState<RowsProp>([]);
451+
452+
const createRandomRows = () => {
453+
const randomRows: any[] = [];
454+
455+
for (let i = 0; i < 10; i += 1) {
456+
const id = randomInt(0, 100000).toString();
457+
randomRows.push({ id, name: 'name test', team: id });
458+
}
459+
460+
setRows(randomRows);
461+
};
462+
463+
return (
464+
<div className="grid-container" style={{ flexDirection: 'column' }}>
465+
<Button onClick={() => createRandomRows()}>Random Rows</Button>
466+
<XGrid rows={rows} columns={columns} sortModel={[{ field: 'team', sort: 'asc' }]} />
467+
</div>
468+
);
469+
};

0 commit comments

Comments
 (0)