diff --git a/docs/List.md b/docs/List.md index 6275346c6e4..9ba682ffe68 100644 --- a/docs/List.md +++ b/docs/List.md @@ -1026,6 +1026,29 @@ const PostList = ({ classes, ...props }) => ( export default withStyles(styles)(PostList); ``` +### Performance + +when displaying large pages of data, you might experience some performance issues. +This is mostly due to the fact that we iterate over the `` children and clone them. + +In such cases, you can opt-in for an optimized version of the `` by setting its `optimized` prop to `true`. +Be aware that you can't have dynamic children, such as those displayed or hidden by checking permissions, when using this mode. + +```jsx +const PostList = props => ( + + + + + + + +); + +export default withStyles(styles)(PostList); +``` + + ## The `` component For mobile devices, a `` is often unusable - there is simply not enough space to display several columns. The convention in that case is to use a simple list, with only one column per row. The `` component serves that purpose, leveraging [material-ui's `` and `` components](https://material-ui.com/demos/lists/). You can use it as `` or `` child: diff --git a/packages/ra-ui-materialui/src/list/Datagrid.js b/packages/ra-ui-materialui/src/list/Datagrid.js index 0d576fbb7a7..0a8a38db097 100644 --- a/packages/ra-ui-materialui/src/list/Datagrid.js +++ b/packages/ra-ui-materialui/src/list/Datagrid.js @@ -15,8 +15,8 @@ import Checkbox from '@material-ui/core/Checkbox'; import classnames from 'classnames'; import DatagridHeaderCell from './DatagridHeaderCell'; -import DatagridBody from './DatagridBody'; import DatagridLoading from './DatagridLoading'; +import DatagridBody, { PureDatagridBody } from './DatagridBody'; const useStyles = makeStyles(theme => ({ table: { @@ -103,7 +103,8 @@ function Datagrid({ classes: classesOverride, ...props }) { const classes = useStyles({ classes: classesOverride }); const { basePath, - body, + optimized = false, + body = optimized ? : , children, className, currentSort, @@ -289,7 +290,6 @@ Datagrid.defaultProps = { hasBulkActions: false, ids: [], selectedIds: [], - body: , }; export default Datagrid; diff --git a/packages/ra-ui-materialui/src/list/DatagridBody.js b/packages/ra-ui-materialui/src/list/DatagridBody.js index 125515f0058..68a27a4fbe0 100644 --- a/packages/ra-ui-materialui/src/list/DatagridBody.js +++ b/packages/ra-ui-materialui/src/list/DatagridBody.js @@ -1,9 +1,10 @@ -import React, { cloneElement, useMemo } from 'react'; +import React, { cloneElement, memo } from 'react'; import PropTypes from 'prop-types'; import TableBody from '@material-ui/core/TableBody'; import classnames from 'classnames'; +import isEqual from 'lodash/isEqual'; -import DatagridRow from './DatagridRow'; +import DatagridRow, { PureDatagridRow } from './DatagridRow'; const DatagridBody = ({ basePath, @@ -24,45 +25,36 @@ const DatagridBody = ({ styles, version, ...rest -}) => - useMemo( - () => ( - - {ids.map((id, rowIndex) => - cloneElement( - row, - { - basePath, - classes, - className: classnames(classes.row, { - [classes.rowEven]: rowIndex % 2 === 0, - [classes.rowOdd]: rowIndex % 2 !== 0, - [classes.clickableRow]: rowClick, - }), - expand, - hasBulkActions, - hover, - id, - key: id, - onToggleItem, - record: data[id], - resource, - rowClick, - selected: selectedIds.includes(id), - style: rowStyle - ? rowStyle(data[id], rowIndex) - : null, - }, - children - ) - )} - - ), - [version, data, selectedIds, JSON.stringify(ids), hasBulkActions] // eslint-disable-line - ); +}) => ( + + {ids.map((id, rowIndex) => + cloneElement( + row, + { + basePath, + classes, + className: classnames(classes.row, { + [classes.rowEven]: rowIndex % 2 === 0, + [classes.rowOdd]: rowIndex % 2 !== 0, + [classes.clickableRow]: rowClick, + }), + expand, + hasBulkActions, + hover, + id, + key: id, + onToggleItem, + record: data[id], + resource, + rowClick, + selected: selectedIds.includes(id), + style: rowStyle ? rowStyle(data[id], rowIndex) : null, + }, + children + ) + )} + +); DatagridBody.propTypes = { basePath: PropTypes.string, @@ -90,8 +82,20 @@ DatagridBody.defaultProps = { ids: [], row: , }; - // trick material-ui Table into thinking this is one of the child type it supports DatagridBody.muiName = 'TableBody'; +const areEqual = (prevProps, nextProps) => { + const { children: _, ...prevPropsWithoutChildren } = prevProps; + const { children: __, ...nextPropsWithoutChildren } = nextProps; + return isEqual(prevPropsWithoutChildren, nextPropsWithoutChildren); +}; + +export const PureDatagridBody = memo(DatagridBody, areEqual); +// trick material-ui Table into thinking this is one of the child type it supports +PureDatagridBody.muiName = 'TableBody'; +PureDatagridBody.defaultProps = { + row: , +}; + export default DatagridBody; diff --git a/packages/ra-ui-materialui/src/list/DatagridRow.js b/packages/ra-ui-materialui/src/list/DatagridRow.js index e57abeb81dd..fce7e09fe44 100644 --- a/packages/ra-ui-materialui/src/list/DatagridRow.js +++ b/packages/ra-ui-materialui/src/list/DatagridRow.js @@ -8,13 +8,13 @@ import React, { useCallback, memo, } from 'react'; -import isEqual from 'lodash/isEqual'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { useDispatch } from 'react-redux'; import { push } from 'connected-react-router'; import { TableCell, TableRow, Checkbox } from '@material-ui/core'; import { linkToRecord } from 'ra-core'; +import isEqual from 'lodash/isEqual'; import DatagridCell from './DatagridCell'; import ExpandRowButton from './ExpandRowButton'; @@ -213,8 +213,8 @@ const areEqual = (prevProps, nextProps) => { return isEqual(prevPropsWithoutChildren, nextPropsWithoutChildren); }; -const PureDatagridRow = memo(DatagridRow, areEqual); +export const PureDatagridRow = memo(DatagridRow, areEqual); PureDatagridRow.displayName = 'PureDatagridRow'; -export default PureDatagridRow; +export default DatagridRow; diff --git a/packages/ra-ui-materialui/src/list/index.js b/packages/ra-ui-materialui/src/list/index.js index 270a05acef8..b78711af8bf 100644 --- a/packages/ra-ui-materialui/src/list/index.js +++ b/packages/ra-ui-materialui/src/list/index.js @@ -2,8 +2,8 @@ import BulkActionsToolbar from './BulkActionsToolbar'; import BulkDeleteAction from './BulkDeleteAction'; import Datagrid from './Datagrid'; import DatagridLoading from './DatagridLoading'; -import DatagridBody from './DatagridBody'; -import DatagridRow from './DatagridRow'; +import DatagridBody, { PureDatagridBody } from './DatagridBody'; +import DatagridRow, { PureDatagridRow } from './DatagridRow'; import DatagridHeaderCell from './DatagridHeaderCell'; import DatagridCell from './DatagridCell'; import Filter from './Filter'; @@ -37,6 +37,8 @@ export { ListToolbar, Pagination, PaginationLimit, + PureDatagridBody, + PureDatagridRow, SimpleList, SingleFieldList, };