immutable data structures in tables 2/n: managed table
Summary: Migrating tables' row collection to Immutable.js List: 1. Change native array to Immutable list in ManagedTable_immutable ----- Current implementation of tables forces to copy arrays on new data arrival which causes O(N^2) complexity -> tables can't handle a lot of new rows in short period of time -> tables freeze and become unresponsive for a few seconds. Immutable data structures will bring us to O(N) complexity Reviewed By: jknoxville Differential Revision: D16416869 fbshipit-source-id: 6d5690d8f5f70286f31a423e319b2cb22deab8ff
This commit is contained in:
committed by
Facebook Github Bot
parent
af7830d94a
commit
6deaf2106f
@@ -11,7 +11,7 @@ import type {
|
||||
TableColumns,
|
||||
TableHighlightedRows,
|
||||
TableRowSortOrder,
|
||||
TableRows,
|
||||
TableRows_immutable,
|
||||
TableBodyRow,
|
||||
TableOnAddFilter,
|
||||
} from './types.js';
|
||||
@@ -33,7 +33,7 @@ import debounce from 'lodash.debounce';
|
||||
import {DEFAULT_ROW_HEIGHT} from './types';
|
||||
import textContent from '../../../utils/textContent.js';
|
||||
|
||||
export type ManagedTableProps = {|
|
||||
export type ManagedTableProps_immutable = {|
|
||||
/**
|
||||
* Column definitions.
|
||||
*/
|
||||
@@ -41,7 +41,7 @@ export type ManagedTableProps = {|
|
||||
/**
|
||||
* Row definitions.
|
||||
*/
|
||||
rows: TableRows,
|
||||
rows: TableRows_immutable,
|
||||
/*
|
||||
* Globally unique key for persisting data between uses of a table such as column sizes.
|
||||
*/
|
||||
@@ -148,7 +148,7 @@ const Container = styled(FlexColumn)(props => ({
|
||||
const globalTableState: {[string]: TableColumnSizes} = {};
|
||||
|
||||
class ManagedTable extends React.Component<
|
||||
ManagedTableProps,
|
||||
ManagedTableProps_immutable,
|
||||
ManagedTableState,
|
||||
> {
|
||||
static defaultProps = {
|
||||
@@ -202,7 +202,7 @@ class ManagedTable extends React.Component<
|
||||
document.removeEventListener('keydown', this.onKeyDown);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: ManagedTableProps) {
|
||||
componentWillReceiveProps(nextProps: ManagedTableProps_immutable) {
|
||||
// if columnSizes has changed
|
||||
if (nextProps.columnSizes !== this.props.columnSizes) {
|
||||
this.setState({
|
||||
@@ -228,7 +228,7 @@ class ManagedTable extends React.Component<
|
||||
}
|
||||
|
||||
if (
|
||||
this.props.rows.length > nextProps.rows.length &&
|
||||
this.props.rows.size > nextProps.rows.size &&
|
||||
this.tableRef &&
|
||||
this.tableRef.current
|
||||
) {
|
||||
@@ -238,11 +238,11 @@ class ManagedTable extends React.Component<
|
||||
}
|
||||
|
||||
componentDidUpdate(
|
||||
prevProps: ManagedTableProps,
|
||||
prevProps: ManagedTableProps_immutable,
|
||||
prevState: ManagedTableState,
|
||||
) {
|
||||
if (
|
||||
this.props.rows.length !== prevProps.rows.length &&
|
||||
this.props.rows.size !== prevProps.rows.size &&
|
||||
this.state.shouldScrollToBottom &&
|
||||
this.state.highlightedRows.size < 2
|
||||
) {
|
||||
@@ -314,13 +314,14 @@ class ManagedTable extends React.Component<
|
||||
row => row.key === lastItemKey,
|
||||
);
|
||||
const newIndex = Math.min(
|
||||
rows.length - 1,
|
||||
rows.size - 1,
|
||||
Math.max(0, e.keyCode === 38 ? lastItemIndex - 1 : lastItemIndex + 1),
|
||||
);
|
||||
if (!e.shiftKey) {
|
||||
highlightedRows.clear();
|
||||
}
|
||||
highlightedRows.add(rows[newIndex].key);
|
||||
// $FlowFixMe 0 <= newIndex <= rows.size - 1
|
||||
highlightedRows.add(rows.get(newIndex).key);
|
||||
this.onRowHighlighted(highlightedRows, () => {
|
||||
const {current} = this.tableRef;
|
||||
if (current) {
|
||||
@@ -374,8 +375,8 @@ class ManagedTable extends React.Component<
|
||||
scrollToBottom() {
|
||||
const {current: tableRef} = this.tableRef;
|
||||
|
||||
if (tableRef && this.props.rows.length > 1) {
|
||||
tableRef.scrollToItem(this.props.rows.length - 1);
|
||||
if (tableRef && this.props.rows.size > 1) {
|
||||
tableRef.scrollToItem(this.props.rows.size - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,11 +438,13 @@ class ManagedTable extends React.Component<
|
||||
const selected = [];
|
||||
let startIndex = -1;
|
||||
let endIndex = -1;
|
||||
for (let i = 0; i < this.props.rows.length; i++) {
|
||||
if (this.props.rows[i].key === fromKey) {
|
||||
for (let i = 0; i < this.props.rows.size; i++) {
|
||||
// $FlowFixMe 0 <= newIndex <= rows.size - 1
|
||||
if (this.props.rows.get(i).key === fromKey) {
|
||||
startIndex = i;
|
||||
}
|
||||
if (this.props.rows[i].key === toKey) {
|
||||
// $FlowFixMe 0 <= newIndex <= rows.size - 1
|
||||
if (this.props.rows.get(i).key === toKey) {
|
||||
endIndex = i;
|
||||
}
|
||||
if (endIndex > -1 && startIndex > -1) {
|
||||
@@ -455,7 +458,8 @@ class ManagedTable extends React.Component<
|
||||
i++
|
||||
) {
|
||||
try {
|
||||
selected.push(this.props.rows[i].key);
|
||||
// $FlowFixMe 0 <= newIndex <= rows.size - 1
|
||||
selected.push(this.props.rows.get(i).key);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -477,7 +481,8 @@ class ManagedTable extends React.Component<
|
||||
!e.shiftKey // When shift key is pressed, it's a range select not a drag select
|
||||
) {
|
||||
current.scrollToItem(index + 1);
|
||||
const startKey = this.props.rows[dragStartIndex].key;
|
||||
// $FlowFixMe 0 <= newIndex <= rows.size - 1
|
||||
const startKey = this.props.rows.get(dragStartIndex).key;
|
||||
const highlightedRows = new Set(this.selectInRange(startKey, row.key));
|
||||
this.onRowHighlighted(highlightedRows);
|
||||
}
|
||||
@@ -618,17 +623,22 @@ class ManagedTable extends React.Component<
|
||||
.map(k => (k.visible ? k.key : null))
|
||||
.filter(Boolean);
|
||||
|
||||
const row = rows.get(index);
|
||||
if (row == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
key={rows[index].key}
|
||||
key={row.key}
|
||||
columnSizes={columnSizes}
|
||||
columnKeys={columnKeys}
|
||||
onMouseDown={e => this.onHighlight(e, rows[index], index)}
|
||||
onMouseEnter={e => this.onMouseEnterRow(e, rows[index], index)}
|
||||
onMouseDown={e => this.onHighlight(e, row, index)}
|
||||
onMouseEnter={e => this.onMouseEnterRow(e, row, index)}
|
||||
multiline={multiline}
|
||||
rowLineHeight={24}
|
||||
highlighted={highlightedRows.has(rows[index].key)}
|
||||
row={rows[index]}
|
||||
highlighted={highlightedRows.has(row.key)}
|
||||
row={row}
|
||||
index={index}
|
||||
style={style}
|
||||
onAddFilter={onAddFilter}
|
||||
@@ -694,9 +704,9 @@ class ManagedTable extends React.Component<
|
||||
this.buildContextMenuItems
|
||||
}>
|
||||
<List
|
||||
itemCount={rows.length}
|
||||
itemCount={rows.size}
|
||||
itemSize={index =>
|
||||
(rows[index] && rows[index].height) ||
|
||||
(rows.get(index) && rows.get(index).height) ||
|
||||
rowLineHeight ||
|
||||
DEFAULT_ROW_HEIGHT
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
import type {Filter} from '../filter/types.js';
|
||||
|
||||
import {List} from 'immutable';
|
||||
|
||||
export const MINIMUM_COLUMN_WIDTH = 100;
|
||||
export const DEFAULT_COLUMN_WIDTH = 200;
|
||||
export const DEFAULT_ROW_HEIGHT = 23;
|
||||
@@ -71,6 +73,8 @@ export type TableColumns = {
|
||||
|
||||
export type TableRows = Array<TableBodyRow>;
|
||||
|
||||
export type TableRows_immutable = List<TableBodyRow>;
|
||||
|
||||
export type TableRowSortOrder = {|
|
||||
key: string,
|
||||
direction: 'up' | 'down',
|
||||
|
||||
@@ -30,6 +30,7 @@ export {default as Popover} from './components/Popover.js';
|
||||
export type {
|
||||
TableColumns,
|
||||
TableRows,
|
||||
TableRows_immutable,
|
||||
TableBodyColumn,
|
||||
TableBodyRow,
|
||||
TableHighlightedRows,
|
||||
@@ -40,6 +41,13 @@ export type {
|
||||
} from './components/table/types.js';
|
||||
export {default as ManagedTable} from './components/table/ManagedTable.js';
|
||||
export type {ManagedTableProps} from './components/table/ManagedTable.js';
|
||||
export {
|
||||
default as ManagedTable_immutable,
|
||||
} from './components/table/ManagedTable_immutable.js';
|
||||
export type {
|
||||
ManagedTableProps_immutable,
|
||||
} from './components/table/ManagedTable_immutable.js';
|
||||
|
||||
export type {Value} from './components/table/TypeBasedValueRenderer.js';
|
||||
export {renderValue} from './components/table/TypeBasedValueRenderer.js';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user