Yix*_*Liu 7 reactjs react-virtualized
我使用 react-virtualized Table 来渲染一个包含多行的表。由于固定的列宽,我不希望我的长文本被修剪,所以我想使用 CellMeasurer 来动态测量宽度。
这是一个使用 Grid 的示例。它工作正常。
render() {
const {width} = this.props;
return (
<Grid
className={styles.BodyGrid}
columnCount={1000}
columnWidth={this._cache.columnWidth}
deferredMeasurementCache={this._cache}
height={400}
overscanColumnCount={0}
overscanRowCount={2}
cellRenderer={this._cellRenderer}
rowCount={50}
rowHeight={35}
width={width}
/>
);
}
Run Code Online (Sandbox Code Playgroud)
但无论是Table也Column有deferredMeasurementCache道具。我当前的代码如下所示:
return (
<div>
<AutoSizer disableHeight>
{({width}) => (
<Table
ref="Table"
disableHeader={disableHeader}
headerClassName={styles.headerColumn}
headerHeight={headerHeight}
height={height}
noRowsRenderer={this._noRowsRenderer}
overscanRowCount={overscanRowCount}
rowClassName={this._rowClassName}
rowHeight={useDynamicRowHeight ? this._getRowHeight : rowHeight}
rowGetter={rowGetter}
rowCount={rowCount}
scrollToIndex={scrollToIndex}
sort={this._sort}
sortBy={sortBy}
sortDirection={sortDirection}
width={width}>
<Column
label="Index"
cellDataGetter={({rowData}) => rowData.index}
dataKey="index"
disableSort={!this._isSortEnabled()}
width={60}
/>
<Column .../>
</Table>
)}
</Autosizer>
</div>
);
Run Code Online (Sandbox Code Playgroud)
如何Measurer在表格中使用?
小智 7
react-virtualized 的文档化 API 不支持CellMeasurer在Table. 这在反应虚拟化中留下了一些选项,包括:
Grid和外部列标题Table依赖于 API 中未记录的内部组件下面概述了后一种方法的解决方案,该方法适用于 react-virtualized v9.21.1(截至 2019 年 7 月的最新版本)。当然,这种方法有风险,即在未来版本的 react-virtualized 中更改内部结构会破坏某些东西。
有几个问题需要处理,包括:
Table在Grid内部使用 a来提供虚拟化滚动,但不会在需要的地方在 API 中公开它。Grid只有一列,其中包含所有的Column一排电池,但Grid作为家长渲染传递Column细胞。其结果是,一个Grid小区可以与许多相关Column的细胞,这种情况Grid和 CellMeasurer不支持。CellMeasurerin a 的使用Grid依赖于Grid直接管理一行中的所有单元格,无需干预rowRenderer,同时Table有自己的行渲染逻辑。[在下面的代码示例中,一些数据元素和函数用模块级声明显示。实际上,它们可以被定义为包含 的组件的成员,Table或者在某些情况下,可能作为道具传递给包含 的组件Table。]
以下解决方案可解决一般情况下的这些问题:
在这种情况下,使用了 CellMeasurerCache 的两个实例。 cellCache是单个Column单元格的高度。rowCache是用于行的高度。
const cellCache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 20, // keep this <= any actual row height
minHeight: 10, // keep this <= any actual row height
});
const rowCache = new CellMeasurerCache({
fixedWidth: true,
defaultHeight: 37, // tune as estimate for unmeasured rows
minHeight: 10, // keep this <= any actual row height
});
Run Code Online (Sandbox Code Playgroud)
对于Table组件:
rowCache为deferredMeasurementCache道具rowRenderer功能rowHeight功能 <Table
...
deferredMeasurementCache={rowCache}
rowRenderer={rowRenderer}
rowHeight={rowHeight}
>
Run Code Online (Sandbox Code Playgroud)
后面会介绍这些功能。 除了将它作为道具传递给's之外,Table不会deferredMeasurementCache对它做任何事情。TableGrid
对于Column需要测量的每一个,添加一个cellRenderer函数。在许多更简单的情况下,相同的函数可用于所有测量列:
<Column
...
cellRenderer={measuredCellRenderer}
/>
Run Code Online (Sandbox Code Playgroud)
为了帮助协调两个缓存的使用,需要三个额外的数据项:
const aMeasuredColumnIndex = 2; // any measured column index will do
let rowParent = null; // let a cellRenderer supply a usable value
const cellParent = { // an intermediary between measured row cells
// and their true containing Grid
invalidateCellSizeAfterRender: ({rowIndex}) => {
if (rowParent &&
typeof rowParent.invalidateCellSizeAfterRender == 'function') {
rowParent.invalidateCellSizeAfterRender({columnIndex: 0, rowIndex});
}
},
}
Run Code Online (Sandbox Code Playgroud)
rowParent用于将表格的网格暴露给 rowRenderer。cellParent充当两个缓存之间以及行、其Column单元格和Table's之间的中介Grid。
接下来是前面提到的三个函数:
function measuredCellRenderer({rowIndex, columnIndex, parent, cellData}) {
rowParent = parent; // parent is the Table's grid,
// save it for use by rowRenderer
return (
<CellMeasurer
cache={cellCache}
columnIndex={columnIndex}
parent={cellParent}
rowIndex={rowIndex}
>
<div>{cellData}</div>
</CellMeasurer>
);
// Note: cellData is wrapped in a <div> to facilitate height
// styling, for example adding padding to the <div>, because CellMeasurer
// measures the height of the content box.
}
function rowRenderer(params) {
return (
<CellMeasurer
cache={rowCache}
columnIndex={0}
key={params.key}
parent={rowParent}
rowIndex={params.rowIndex}
>
{Table.defaultProps.rowRenderer(params)}
</CellMeasurer>
);
}
function rowHeight({index}) {
let cellCacheRowHeight = cellCache.rowHeight({index});
if (cellCache.has(index, aMeasuredColumnIndex)) {
rowCache.set(index, 0, 20, cellCacheRowHeight);
// the 20 above is a somewhat arbitrary number for width,
// which is not relevant
}
return cellCacheRowHeight;
}
Run Code Online (Sandbox Code Playgroud)
请注意, 有两种不同的用法CellMeasurer。一种是在measuredCellRenderer函数内部并使用cellCacheand cellParent。另一个在rowRenderer函数内部并使用rowCacheand rowParent。
此外,该rowHeight函数不仅报告行的高度。它还负责将 .inrowHeight中cellCache第一列也是唯一一列的行的输入转换为行的单元格高度rowCache。
当表只有一个测量列时,可以稍微简化此解决方案。只CellMeasurerCache需要一个。单个高速缓存可以实现两者的作用 cellCache和rowCache。其结果:
cellParent;它可以被移除。对的引用cellParent可以替换为对 的引用rowParent,或者在measuredCellRenderer函数的情况下,CellMeasurer parent可以将 prop 直接设置为parent函数参数。measuredCellRenderer,CellMeasurer需要对 进行硬编码columnIndex={0},即使测量列不是表中的第一列。if内部声明rowHeight函数可以被删除,因为没有需要转移之间的二级缓存的高度。aMeasuredColumnIndex可以删除,因为它是在唯一的参考rowHeight if声明。