From dffe588f6693b84581695158024b109230866b00 Mon Sep 17 00:00:00 2001 From: AllenFang Date: Sat, 1 Sep 2018 12:56:57 +0800 Subject: [PATCH] fix cell level performace, remain select all --- .../react-bootstrap-table2-editor/index.js | 2 - .../src/cell-binder.js | 38 ----- .../src/context.js | 1 - .../src/editing-cell-binder.js | 3 +- .../src/row-binder.js | 5 +- .../examples/basic/large-table.js | 3 +- packages/react-bootstrap-table2/src/body.js | 153 +++++++++--------- .../src/row-aggregator.js | 15 +- .../src/row-selection/row-binder.js | 2 +- .../src/row-selection/selection-cell.js | 8 +- .../src/row-should-updater.js | 25 +++ packages/react-bootstrap-table2/src/row.js | 41 ++++- 12 files changed, 165 insertions(+), 131 deletions(-) delete mode 100644 packages/react-bootstrap-table2-editor/src/cell-binder.js create mode 100644 packages/react-bootstrap-table2/src/row-should-updater.js diff --git a/packages/react-bootstrap-table2-editor/index.js b/packages/react-bootstrap-table2-editor/index.js index a4b4e65..fc8439d 100644 --- a/packages/react-bootstrap-table2-editor/index.js +++ b/packages/react-bootstrap-table2-editor/index.js @@ -1,5 +1,4 @@ import createContext from './src/context'; -import bindCellLevelCellEdit from './src/cell-binder'; import bindRowLevelCellEdit from './src/row-binder'; import createEditingCell from './src/editing-cell-binder'; import { @@ -11,7 +10,6 @@ import { export default (options = {}) => ({ createContext, createEditingCell, - bindCellLevelCellEdit, bindRowLevelCellEdit, DBCLICK_TO_CELL_EDIT, DELAY_FOR_DBCLICK, diff --git a/packages/react-bootstrap-table2-editor/src/cell-binder.js b/packages/react-bootstrap-table2-editor/src/cell-binder.js deleted file mode 100644 index 6ef110a..0000000 --- a/packages/react-bootstrap-table2-editor/src/cell-binder.js +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint react/prop-types: 0 */ -import React from 'react'; -import { CLICK_TO_CELL_EDIT, DBCLICK_TO_CELL_EDIT } from './const'; - -import { Consumer } from './context'; - -export default (Component, keyField, _) => { - const renderWithCellEdit = (props, cellEdit) => { - const content = _.get(props.row, props.column.dataField); - const editableRow = props.editable; - - let editable = _.isDefined(props.column.editable) ? props.column.editable : true; - if (props.column.dataField === keyField || !editableRow) editable = false; - if (_.isFunction(props.column.editable)) { - editable = props.column.editable( - content, - props.row, - props.rowIndex, - props.columnIndex - ); - } - - return ( - - ); - }; - return props => ( - - { cellEdit => renderWithCellEdit(props, cellEdit) } - - ); -}; diff --git a/packages/react-bootstrap-table2-editor/src/context.js b/packages/react-bootstrap-table2-editor/src/context.js index 7011137..c3f37c5 100644 --- a/packages/react-bootstrap-table2-editor/src/context.js +++ b/packages/react-bootstrap-table2-editor/src/context.js @@ -100,7 +100,6 @@ export default ( const { cellEdit: { options: { nonEditableRows, errorMessage, ...optionsRest }, - createContext, ...cellEditRest } } = this.props; diff --git a/packages/react-bootstrap-table2-editor/src/editing-cell-binder.js b/packages/react-bootstrap-table2-editor/src/editing-cell-binder.js index 6d3b079..0a9ef77 100644 --- a/packages/react-bootstrap-table2-editor/src/editing-cell-binder.js +++ b/packages/react-bootstrap-table2-editor/src/editing-cell-binder.js @@ -4,6 +4,7 @@ import { Consumer } from './context'; import createEditingCell from './editing-cell'; export default (_) => { + const EditingCell = createEditingCell(_); const renderWithEditingCell = (props, cellEdit) => { const content = _.get(props.row, props.column.dataField); let editCellstyle = props.column.editCellStyle || {}; @@ -24,7 +25,7 @@ export default (_) => { props.columnIndex) ; } - const EditingCell = createEditingCell(_); + return ( { @@ -24,6 +24,9 @@ export default (Component, selectRowEnabled) => { editingRowIdx={ cellEdit.ridx } editingColIdx={ cellEdit.cidx } editable={ editableRow } + onStart={ cellEdit.onStart } + clickToEdit={ cellEdit.mode === CLICK_TO_CELL_EDIT } + dbclickToEdit={ cellEdit.mode === DBCLICK_TO_CELL_EDIT } /> ); }; diff --git a/packages/react-bootstrap-table2-example/examples/basic/large-table.js b/packages/react-bootstrap-table2-example/examples/basic/large-table.js index 671aaa5..f25beae 100644 --- a/packages/react-bootstrap-table2-example/examples/basic/large-table.js +++ b/packages/react-bootstrap-table2-example/examples/basic/large-table.js @@ -4,7 +4,7 @@ import BootstrapTable from 'react-bootstrap-table-next'; import cellEditFactory from 'react-bootstrap-table2-editor'; import { productsGenerator } from 'utils/common'; -const products = productsGenerator(5000); +const products = productsGenerator(5); const columns = [{ dataField: 'id', @@ -23,7 +23,6 @@ export default () => ( keyField="id" data={ products } columns={ columns } - selectRow={ { mode: 'checkbox' } } cellEdit={ cellEditFactory({ mode: 'click' }) } diff --git a/packages/react-bootstrap-table2/src/body.js b/packages/react-bootstrap-table2/src/body.js index 03582ce..8c83eaa 100644 --- a/packages/react-bootstrap-table2/src/body.js +++ b/packages/react-bootstrap-table2/src/body.js @@ -6,89 +6,94 @@ import PropTypes from 'prop-types'; import _ from './utils'; import Row from './row'; -import Cell from './cell'; import RowAggregator from './row-aggregator'; import RowSection from './row-section'; import Const from './const'; import bindSelection from './row-selection/row-binder'; import bindExpansion from './row-expand/row-binder'; -const Body = (props) => { - const { - columns, - data, - keyField, - isEmpty, - noDataIndication, - visibleColumnSize, - cellEdit, - selectRow, - rowStyle, - rowClasses, - rowEvents, - expandRow - } = props; - - let content; - - if (isEmpty) { - const indication = _.isFunction(noDataIndication) ? noDataIndication() : noDataIndication; - if (!indication) { - return null; +class Body extends React.Component { + constructor(props) { + super(props); + if (props.cellEdit.createContext) { + this.EditingCell = props.cellEdit.createEditingCell(_, props.cellEdit.options.onStartEdit); } - content = ; - } else { - let RowComponent = Row; - const selectRowEnabled = selectRow.mode !== Const.ROW_SELECT_DISABLED; - const expandRowEnabled = !!expandRow.renderer; - - const additionalRowProps = {}; - if (expandRowEnabled) { - RowComponent = bindExpansion(RowAggregator, visibleColumnSize); - } - - if (selectRowEnabled) { - RowComponent = bindSelection(expandRowEnabled ? RowComponent : RowAggregator); - } - - if (cellEdit.createContext) { - const CellComponent = cellEdit.bindCellLevelCellEdit(Cell, keyField, _); - const EditingCell = cellEdit.createEditingCell(_, cellEdit.options.onStartEdit); - RowComponent = cellEdit.bindRowLevelCellEdit(RowComponent, selectRowEnabled); - additionalRowProps.CellComponent = CellComponent; - additionalRowProps.EditingCellComponent = EditingCell; - } - - if (selectRowEnabled || expandRowEnabled) { - additionalRowProps.expandRow = expandRow; - additionalRowProps.selectRow = selectRow; - } - - content = data.map((row, index) => { - const key = _.get(row, keyField); - const baseRowProps = { - key, - row, - columns, - keyField, - cellEdit, - value: key, - rowIndex: index, - attrs: rowEvents || {}, - ...additionalRowProps - }; - - baseRowProps.style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle; - baseRowProps.className = (_.isFunction(rowClasses) ? rowClasses(row, index) : rowClasses); - - return ; - }); } - return ( - { content } - ); -}; + render() { + const { + columns, + data, + keyField, + isEmpty, + noDataIndication, + visibleColumnSize, + cellEdit, + selectRow, + rowStyle, + rowClasses, + rowEvents, + expandRow + } = this.props; + + let content; + + if (isEmpty) { + const indication = _.isFunction(noDataIndication) ? noDataIndication() : noDataIndication; + if (!indication) { + return null; + } + content = ; + } else { + let RowComponent = Row; + const selectRowEnabled = selectRow.mode !== Const.ROW_SELECT_DISABLED; + const expandRowEnabled = !!expandRow.renderer; + + const additionalRowProps = {}; + if (expandRowEnabled) { + RowComponent = bindExpansion(RowAggregator, visibleColumnSize); + } + + if (selectRowEnabled) { + RowComponent = bindSelection(expandRowEnabled ? RowComponent : RowAggregator); + } + + if (cellEdit.createContext) { + RowComponent = cellEdit.bindRowLevelCellEdit(RowComponent, selectRowEnabled, keyField, _); + additionalRowProps.EditingCellComponent = this.EditingCell; + } + + if (selectRowEnabled || expandRowEnabled) { + additionalRowProps.expandRow = expandRow; + additionalRowProps.selectRow = selectRow; + } + + content = data.map((row, index) => { + const key = _.get(row, keyField); + const baseRowProps = { + key, + row, + columns, + keyField, + cellEdit, + value: key, + rowIndex: index, + attrs: rowEvents || {}, + ...additionalRowProps + }; + + baseRowProps.style = _.isFunction(rowStyle) ? rowStyle(row, index) : rowStyle; + baseRowProps.className = (_.isFunction(rowClasses) ? rowClasses(row, index) : rowClasses); + + return ; + }); + } + + return ( + { content } + ); + } +} Body.propTypes = { keyField: PropTypes.string.isRequired, diff --git a/packages/react-bootstrap-table2/src/row-aggregator.js b/packages/react-bootstrap-table2/src/row-aggregator.js index 5d709c5..f9438b3 100644 --- a/packages/react-bootstrap-table2/src/row-aggregator.js +++ b/packages/react-bootstrap-table2/src/row-aggregator.js @@ -6,8 +6,9 @@ import _ from './utils'; import Row from './row'; import ExpandCell from './row-expand/expand-cell'; import SelectionCell from './row-selection/selection-cell'; +import shouldRowUpdater from './row-should-updater'; -export default class RowAggregator extends React.Component { +export default class RowAggregator extends shouldRowUpdater(React.Component) { static propTypes = { attrs: PropTypes.object } @@ -21,6 +22,17 @@ export default class RowAggregator extends React.Component { this.createClickEventHandler = this.createClickEventHandler.bind(this); } + shouldComponentUpdate(nextProps) { + const shouldUpdate = + this.props.selected !== nextProps.selected || + this.props.expanded !== nextProps.expanded || + this.props.selectable !== nextProps.selectable || + this.shouldUpdateByWhenEditing(nextProps) || + this.shouldUpdatedByNormalProps(nextProps); + + return shouldUpdate; + } + createClickEventHandler(cb) { return (e) => { const { @@ -91,6 +103,7 @@ export default class RowAggregator extends React.Component { return ( { ...style, ...selectedStyle }; - className = cs(className, selectedClasses); + className = cs(className, selectedClasses) || undefined; if (selectRow.bgColor) { style = style || {}; diff --git a/packages/react-bootstrap-table2/src/row-selection/selection-cell.js b/packages/react-bootstrap-table2/src/row-selection/selection-cell.js index cdd6bbd..8d46da1 100644 --- a/packages/react-bootstrap-table2/src/row-selection/selection-cell.js +++ b/packages/react-bootstrap-table2/src/row-selection/selection-cell.js @@ -25,9 +25,13 @@ export default class SelectionCell extends Component { } shouldComponentUpdate(nextProps) { - const { selected } = this.props; + const shouldUpdate = + this.props.rowIndex !== nextProps.rowIndex || + this.props.selected !== nextProps.selected || + this.props.disabled !== nextProps.disabled || + this.props.rowKey !== nextProps.rowKey; - return nextProps.selected !== selected; + return shouldUpdate; } handleClick(e) { diff --git a/packages/react-bootstrap-table2/src/row-should-updater.js b/packages/react-bootstrap-table2/src/row-should-updater.js new file mode 100644 index 0000000..17c9947 --- /dev/null +++ b/packages/react-bootstrap-table2/src/row-should-updater.js @@ -0,0 +1,25 @@ +/* eslint react/prop-types: 0 */ +import _ from './utils'; + +export default ExtendBase => + class RowShouldUpdater extends ExtendBase { + shouldUpdateByWhenEditing(nextProps) { + return ( + nextProps.editingRowIdx === nextProps.rowIndex || + (this.props.editingRowIdx === nextProps.rowIndex && + nextProps.editingRowIdx === null) + ); + } + + shouldUpdatedByNormalProps(nextProps) { + const shouldUpdate = + this.props.rowIndex !== nextProps.rowIndex || + this.props.className !== nextProps.className || + this.props.editable !== nextProps.editable || + this.props.columns.length !== nextProps.columns.length || + !_.isEqual(this.props.row, nextProps.row) || + !_.isEqual(this.props.style, nextProps.style); + + return shouldUpdate; + } + }; diff --git a/packages/react-bootstrap-table2/src/row.js b/packages/react-bootstrap-table2/src/row.js index c41da6c..f04caed 100644 --- a/packages/react-bootstrap-table2/src/row.js +++ b/packages/react-bootstrap-table2/src/row.js @@ -6,11 +6,23 @@ import PropTypes from 'prop-types'; import _ from './utils'; import Cell from './cell'; import eventDelegater from './row-event-delegater'; +import shouldRowUpdater from './row-should-updater'; + +class Row extends shouldRowUpdater(eventDelegater(Component)) { + shouldComponentUpdate(nextProps) { + console.log('lol'); + const shouldUpdate = + nextProps.shouldUpdate || + this.shouldUpdateByWhenEditing(nextProps) || + this.shouldUpdatedByNormalProps(nextProps); + + return shouldUpdate; + } -class Row extends eventDelegater(Component) { render() { const { row, + keyField, columns, rowIndex, className, @@ -18,9 +30,11 @@ class Row extends eventDelegater(Component) { attrs, editable, editingRowIdx, - editingColIdx + editingColIdx, + onStart, + clickToEdit, + dbclickToEdit } = this.props; - const CellComponent = this.props.CellComponent || Cell; const trAttrs = this.delegate(attrs); return ( @@ -35,7 +49,7 @@ class Row extends eventDelegater(Component) { const EditingCell = this.props.EditingCellComponent; return ( ); @@ -108,14 +131,16 @@ Row.propTypes = { columns: PropTypes.array.isRequired, style: PropTypes.object, className: PropTypes.string, - attrs: PropTypes.object + attrs: PropTypes.object, + shouldUpdate: PropTypes.bool }; Row.defaultProps = { editable: true, style: {}, className: null, - attrs: {} + attrs: {}, + shouldUpdate: false }; export default Row;