Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataGrid] Scroll performance improvements #9037

Merged
merged 74 commits into from
Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
fc48f10
feat: add react store
romgrk May 18, 2023
8c81c06
refactor
romgrk May 18, 2023
0d1024a
perf: memoize rows & headers
romgrk May 24, 2023
33d0998
test: make them pass
romgrk May 24, 2023
d6f7777
Merge branch 'master' into perf-scroll-improvements
romgrk May 24, 2023
f8e7691
lint
romgrk May 24, 2023
c6455d9
lint
romgrk May 24, 2023
271c9bd
lint
romgrk May 24, 2023
a63e8e4
lint
romgrk May 24, 2023
6d8364d
lint
romgrk May 24, 2023
a81488b
doc: update performance.md
romgrk May 24, 2023
baa5dca
fix: add enough storage for the memoization cache
romgrk May 24, 2023
83394e7
fix: missing update
romgrk May 25, 2023
15df13b
test: keyboard
romgrk May 25, 2023
8db4508
lint
romgrk May 25, 2023
138dba7
docs: autogenerate
romgrk May 25, 2023
fa0fc7e
lint
romgrk May 25, 2023
e70c982
lint
romgrk May 25, 2023
3b81c02
lint
romgrk May 25, 2023
8f76caa
refactor
romgrk May 29, 2023
de7486f
refactor
romgrk May 29, 2023
4ff14c7
fix
romgrk May 29, 2023
c47bf23
lint
romgrk May 29, 2023
5ac0d68
lint
romgrk May 29, 2023
91aefeb
lint
romgrk May 29, 2023
56e73e7
lint
romgrk May 29, 2023
42b9b72
refactor
romgrk May 29, 2023
2105746
lint
romgrk May 30, 2023
5e1b182
lint# Changes to be committed:
romgrk May 30, 2023
d804c32
doc
romgrk May 30, 2023
300f8c6
refactor
romgrk May 30, 2023
6ee63fd
lint
romgrk May 30, 2023
04e8f35
perf: improve selector
romgrk Jun 1, 2023
ab456dd
Revert "perf: improve selector"
romgrk Jun 1, 2023
b978077
lint
romgrk Jun 1, 2023
bfad5f8
lint
romgrk Jun 1, 2023
a2052c5
build
romgrk Jun 1, 2023
9d9b0a7
perf: avoid costly update
romgrk Jun 2, 2023
c3c1a41
Revert "perf: avoid costly update"
romgrk Jun 2, 2023
d7eb7ef
refactor
romgrk Jun 2, 2023
ebc5156
refactor# Changes to be committed:
romgrk Jun 2, 2023
21d225f
perf: avoid allocations & memo
romgrk May 15, 2023
0cb952a
perf: *fast* object compare
romgrk Jun 2, 2023
9332b36
perf: fast memo
romgrk Jun 2, 2023
085a232
lint
romgrk May 15, 2023
afe9727
lint
romgrk May 15, 2023
5cd1e04
perf: more fast
romgrk Jun 2, 2023
27fef43
lint
romgrk Jun 6, 2023
3b46c8c
lint
romgrk Jun 6, 2023
fbdeb31
perf: small improvements
romgrk Jun 6, 2023
52deddf
perf: cellv7 single component
romgrk Jun 6, 2023
e58fdd9
lint
romgrk Jun 6, 2023
e0ac8bd
lint
romgrk Jun 6, 2023
1cb49d1
lint
romgrk Jun 6, 2023
7531379
lint
romgrk Jun 6, 2023
f0ee843
fix: missing classnames
romgrk Jun 6, 2023
e6d305c
fix: cell slotProps
romgrk Jun 6, 2023
e49394c
test: fix
romgrk Jun 6, 2023
971cf32
fix: make things work
romgrk Jun 6, 2023
5c884e7
lint: proptypes
romgrk Jun 6, 2023
1d611c0
lint
romgrk Jun 6, 2023
2d419e7
refactor: shallowCompare => objectShallowCompare
romgrk Jun 7, 2023
bab0887
refactor: dont export GridCellV7
romgrk Jun 8, 2023
3db4b83
refactor
romgrk Jun 8, 2023
659d966
lint
romgrk Jun 8, 2023
c37f2af
lint
romgrk Jun 9, 2023
6e06534
doc: update
romgrk Jun 9, 2023
a68f273
lint
romgrk Jun 9, 2023
494d290
lint
romgrk Jun 9, 2023
0169a5a
ci: run
romgrk Jun 12, 2023
0b8795e
Merge branch 'master' into perf-scroll-improvements
romgrk Jun 13, 2023
096b735
Update docs/data/data-grid/state/state.md
romgrk Jun 13, 2023
2028f8f
lint
romgrk Jun 13, 2023
d583d74
fix: column props propagation delay
romgrk Jun 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 0 additions & 79 deletions docs/data/data-grid/performance/GridWithReactMemo.js

This file was deleted.

79 changes: 0 additions & 79 deletions docs/data/data-grid/performance/GridWithReactMemo.tsx

This file was deleted.

10 changes: 0 additions & 10 deletions docs/data/data-grid/performance/GridWithReactMemo.tsx.preview

This file was deleted.

69 changes: 39 additions & 30 deletions docs/data/data-grid/performance/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,52 @@

romgrk marked this conversation as resolved.
Show resolved Hide resolved
<p class="description">Improve the performance of the DataGrid using the recommendations from this guide.</p>

## Memoize inner components with `React.memo`
## Extract static objects and memoize root props
romgrk marked this conversation as resolved.
Show resolved Hide resolved

The `DataGrid` component is composed of a central state object where all data is stored.
When an API method is called, a prop changes, or the user interacts with the UI (e.g. filtering a column), this state object is updated with the changes made.
To reflect the changes in the interface, the component must re-render.
Since the state behaves like `React.useState`, the `DataGrid` component will re-render its children, including column headers, rows, and cells.
With smaller datasets, this is not a problem for concern, but it can become a bottleneck if the number of rows increases, especially if many columns render [custom content](/x/react-data-grid/column-definition/#rendering-cells).
One way to overcome this issue is using `React.memo` to only re-render the child components when their props have changed.
To start using memoization, import the inner components, then pass their memoized version to the respective slots, as follow:
The `DataGrid` component uses `React.memo` to optimize its performance, which means itself and its subcomponents only
romgrk marked this conversation as resolved.
Show resolved Hide resolved
re-render when their props change. But it's very easy to cause unnecessary re-renders if the root props of your
`DataGrid` aren't memoized. Take the example below, the `slots` and `initialState` objects are re-created on every
render, which means the `DataGrid` itself has no choice but to re-render as well.

```tsx
import {
GridRow,
GridColumnHeaders,
DataGrid, // or DataGridPro, DataGridPremium
} from '@mui/x-data-grid';

const MemoizedRow = React.memo(GridRow);
const MemoizedColumnHeaders = React.memo(GridColumnHeaders);

<DataGrid
slots={{
row: MemoizedRow,
columnHeaders: MemoizedColumnHeaders,
}}
/>;
function Component({ rows }) {
return (
<DataGrid
rows={rows}
slots={{
row: CustomRow,
}}
cellModesModel={{ [rows[0].id]: { name: { mode: GridCellModes.Edit } } }}
/>
);
}

```

The following demo show this trick in action.
It also contains additional logic to highlight the components when they re-render.
An easy way to prevent re-renders is to extract any object that can be a static object, and to memoize any object that
depends on another object. This applies to any prop that is an object or a function.

{{"demo": "GridWithReactMemo.js", "bg": "inline", "defaultCodeOpen": false}}
cherniavskii marked this conversation as resolved.
Show resolved Hide resolved
```tsx
const slots = {
row: CustomRow,
};

function Component({ rows }) {
const cellModesModel = React.useMemo(() =>
({ [rows[0].id]: { name: { mode: GridCellModes.Edit } } }),
[rows]
);

return (
<DataGrid
rows={rows}
slots={slots}
cellModesModel={cellModesModel}
/>
);
}

:::warning
We do not ship the components above already wrapped with `React.memo` because if you have rows whose cells display custom content not derived from the received props, e.g. selectors, these cells may display outdated information.
If you define a column with a custom cell renderer where content comes from a [selector](/x/react-data-grid/state/#catalog-of-selectors) that changes more often than the props passed to `GridRow`, the row component should not be memoized.
:::
```

## API

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,12 @@ describe('<DataGridPro /> - Rows', () => {
);
}

// For some reason the number of renders in test env is 2x the number of renders in the browser
const renrederMultiplier = 2;

render(<Test />);
const initialRendersCount = 2;
expect(renderCellSpy.callCount).to.equal(initialRendersCount * renrederMultiplier);
expect(renderCellSpy.callCount).to.equal(initialRendersCount);

act(() => apiRef.current.updateRows([{ id: 1, name: 'John' }]));
expect(renderCellSpy.callCount).to.equal((initialRendersCount + 2) * renrederMultiplier);
expect(renderCellSpy.callCount).to.equal(initialRendersCount + 2);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,6 @@ GridColumnHeaders.propTypes = {
visibleColumns: PropTypes.arrayOf(PropTypes.object).isRequired,
} as any;

export { GridColumnHeaders };
const MemoizedGridColumnHeaders = React.memo(GridColumnHeaders);

export { MemoizedGridColumnHeaders as GridColumnHeaders };
Loading