Selection

There is a PR to add selection feature, but I don't want to merge it with good reasons, so I'd like to share a recipe here for reference

Recipe

const StyledTable = styled(BaseTable)`
.row-selected {
background-color: #e3e3e3;
}
`;
class SelectionCell extends React.PureComponent {
_handleChange = e => {
const { rowData, rowIndex, column } = this.props;
const { onChange } = column;
onChange({ selected: e.target.checked, rowData, rowIndex });
};
render() {
const { rowData, column } = this.props;
const { selectedRowKeys, rowKey } = column;
const checked = selectedRowKeys.includes(rowData[rowKey]);
return <input type="checkbox" checked={checked} onChange={this._handleChange} />;
}
}
class SelectableTable extends React.PureComponent {
constructor(props) {
super(props);
const { selectedRowKeys, defaultSelectedRowKeys, expandedRowKeys, defaultExpandedRowKeys } = props;
this.state = {
selectedRowKeys: (selectedRowKeys !== undefined ? selectedRowKeys : defaultSelectedRowKeys) || [],
expandedRowKeys: (expandedRowKeys !== undefined ? expandedRowKeys : defaultExpandedRowKeys) || [],
};
}
/**
* Set `selectedRowKeys` manually.
* This method is available only if `selectedRowKeys` is uncontrolled.
*
* @param {array} selectedRowKeys
*/
setSelectedRowKeys(selectedRowKeys) {
// if `selectedRowKeys` is controlled
if (this.props.selectedRowKeys !== undefined) return;
this.setState({
selectedRowKeys: cloneArray(selectedRowKeys),
});
}
/**
* See BaseTable#setExpandedRowKeys
*/
setExpandedRowKeys(expandedRowKeys) {
// if `expandedRowKeys` is controlled
if (this.props.expandedRowKeys !== undefined) return;
this.setState({
expandedRowKeys: cloneArray(expandedRowKeys),
});
}
/* some other custom methods and proxy methods */
/**
* Remove rowKeys from inner state manually, it's useful to purge dirty state after rows removed.
* This method is available only if `selectedRowKeys` or `expandedRowKeys` is uncontrolled.
*
* @param {array} rowKeys
*/
removeRowKeysFromState(rowKeys) {
if (!Array.isArray(rowKeys)) return;
const state = {};
if (this.props.selectedRowKeys === undefined && this.state.selectedRowKeys.length > 0) {
state.selectedRowKeys = this.state.selectedRowKeys.filter(key => !rowKeys.includes(key));
}
if (this.props.expandedRowKeys === undefined && this.state.expandedRowKeys.length > 0) {
state.expandedRowKeys = this.state.expandedRowKeys.filter(key => !rowKeys.includes(key));
}
if (state.selectedRowKeys || state.expandedRowKeys) {
this.setState(state);
}
}
_handleSelectChange = ({ selected, rowData, rowIndex }) => {
const selectedRowKeys = [...this.state.selectedRowKeys];
const key = rowData[this.props.rowKey];
if (selected) {
if (!selectedRowKeys.includes(key)) selectedRowKeys.push(key);
} else {
const index = selectedRowKeys.indexOf(key);
if (index > -1) {
selectedRowKeys.splice(index, 1);
}
}
// if `selectedRowKeys` is uncontrolled, update internal state
if (this.props.selectedRowKeys === undefined) {
this.setState({ selectedRowKeys });
}
this.props.onRowSelect({ selected, rowData, rowIndex });
this.props.onSelectedRowsChange(selectedRowKeys);
};
_rowClassName = ({ rowData, rowIndex }) => {
const { rowClassName, rowKey } = this.props;
const { selectedRowKeys } = this.state;
const rowClass = rowClassName ? callOrReturn(rowClassName, { rowData, rowIndex }) : '';
const key = rowData[rowKey];
return [rowClass, selectedRowKeys.includes(key) && 'row-selected'].filter(Boolean).concat(' ');
};
render() {
const { columns, children, selectable, selectionColumnProps, ...rest } = this.props;
const { selectedRowKeys } = this.state;
// you'd better memoize this operation
let _columns = columns || normalizeColumns(children);
if (selectable) {
const selectionColumn = {
width: 40,
flexShrink: 0,
resizable: false,
frozen: Column.FrozenDirection.LEFT,
cellRenderer: SelectionCell,
...selectionColumnProps,
key: '__selection__',
rowKey: this.props.rowKey,
selectedRowKeys: selectedRowKeys,
onChange: this._handleSelectChange,
};
_columns = [selectionColumn, ..._columns];
}
return <StyledTable {...rest} columns={_columns} rowClassName={this._rowClassName} />;
}
}
SelectableTable.defaultProps = {
...BaseTable.defaultProps,
onRowSelect: noop,
onSelectedRowsChange: noop,
};

Example

Check the live example here.