immutable data structures in tables 3/n: searchable table

Summary:
Migrating tables' row collection to Immutable.js List:
1. Сopy SearchableTable code to SearchableTable_immutable
2. Use ManagedTable_immutable for SearchableTable_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: D16416732

fbshipit-source-id: 856ba0407bfdd12bb1b90110e130562a07cc5060
This commit is contained in:
Timur Valiev
2019-07-23 07:57:00 -07:00
committed by Facebook Github Bot
parent 6deaf2106f
commit d3658f7d31
2 changed files with 149 additions and 0 deletions

View File

@@ -0,0 +1,146 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import type {ManagedTableProps_immutable, TableBodyRow, Filter} from 'flipper';
import type {SearchableProps} from './Searchable.js';
import {PureComponent} from 'react';
import ManagedTable_immutable from '../table/ManagedTable_immutable.js';
import textContent from '../../../utils/textContent.js';
import Searchable from './Searchable.js';
import deepEqual from 'deep-equal';
type Props = {|
...ManagedTableProps_immutable,
...SearchableProps,
/** Reference to the table */
innerRef?: (ref: React.ElementRef<*>) => void,
/** Filters that are added to the filterbar by default */
defaultFilters: Array<Filter>,
|};
type State = {
filterRows: (row: TableBodyRow) => boolean,
};
const rowMatchesFilters = (filters: Array<Filter>, row: TableBodyRow) =>
filters
.map((filter: Filter) => {
if (filter.type === 'enum' && row.type != null) {
return filter.value.length === 0 || filter.value.indexOf(row.type) > -1;
} else if (filter.type === 'include') {
return (
textContent(row.columns[filter.key].value).toLowerCase() ===
filter.value.toLowerCase()
);
} else if (filter.type === 'exclude') {
return (
textContent(row.columns[filter.key].value).toLowerCase() !==
filter.value.toLowerCase()
);
} else {
return true;
}
})
.every(x => x === true);
function rowMatchesRegex(values: Array<string>, regex: string): boolean {
try {
const re = new RegExp(regex);
return values.some(x => re.test(x));
} catch (e) {
return false;
}
}
function rowMatchesSearchTerm(
searchTerm: string,
isRegex: boolean,
row: TableBodyRow,
): boolean {
if (searchTerm == null || searchTerm.length === 0) {
return true;
}
const rowValues = Object.keys(row.columns).map(key =>
textContent(row.columns[key].value),
);
if (isRegex) {
return rowMatchesRegex(rowValues, searchTerm);
}
return rowValues.some(x =>
x.toLowerCase().includes(searchTerm.toLowerCase()),
);
}
const filterRowsFactory = (
filters: Array<Filter>,
searchTerm: string,
regexSearch: boolean,
) => (row: TableBodyRow): boolean =>
rowMatchesFilters(filters, row) &&
rowMatchesSearchTerm(searchTerm, regexSearch, row);
class SearchableManagedTable_immutable extends PureComponent<Props, State> {
static defaultProps = {
defaultFilters: [],
};
state = {
filterRows: filterRowsFactory(
this.props.filters,
this.props.searchTerm,
this.props.regexEnabled || false,
),
};
componentDidMount() {
this.props.defaultFilters.map(this.props.addFilter);
}
componentWillReceiveProps(nextProps: Props) {
if (
nextProps.searchTerm !== this.props.searchTerm ||
nextProps.regexEnabled != this.props.regexEnabled ||
!deepEqual(this.props.filters, nextProps.filters)
) {
this.setState({
filterRows: filterRowsFactory(
nextProps.filters,
nextProps.searchTerm,
nextProps.regexEnabled || false,
),
});
}
}
render() {
const {
addFilter,
searchTerm: _searchTerm,
filters: _filters,
innerRef,
rows,
...props
} = this.props;
return (
<ManagedTable_immutable
{...props}
filter={this.state.filterRows}
rows={rows.filter(this.state.filterRows)}
onAddFilter={addFilter}
ref={innerRef}
/>
);
}
}
/**
* Table with filter and searchbar, supports all properties a ManagedTable
* and Searchable supports.
*/
export default Searchable(SearchableManagedTable_immutable);

View File

@@ -158,6 +158,9 @@ export {
export {
default as SearchableTable,
} from './components/searchable/SearchableTable.js';
export {
default as SearchableTable_immutable,
} from './components/searchable/SearchableTable_immutable.js';
export type {SearchableProps} from './components/searchable/Searchable.js';
//