Документированный API для react-virtualized не поддерживает использование CellMeasurer
в Table
. Таким образом, остается несколько вариантов в рамках react-virtualized, в том числе:
- реализовать, используя
Grid
и внешние заголовки столбцов
- реализовать с использованием
Table
с зависимостями от внутренних компонентов, не задокументированных в API
Ниже описывается решение последнего подхода, которое работает с реактивной виртуализацией v9.21.1 (последняя версия по состоянию на июль 2019 г.). Конечно, такой подход рискует, что изменения во внутреннем устройстве в будущих версиях react-virtualized что-то сломают.
Необходимо решить несколько проблем, в том числе:
Table
использует Grid
внутри для обеспечения виртуализированной прокрутки, но не показывает его в API везде, где это необходимо.
Grid
имеет только один столбец, который содержит все Column
ячеек в строке, но Grid
передается как родительский для отрисовки Column
ячеек. В результате одна Grid
ячейка может быть связана со множеством Column
ячеек, что не поддерживается Grid
и CellMeasurer
.
- Использование
CellMeasurer
в 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
});
Для компонента Table
:
- добавить
rowCache
как deferredMeasurementCache
опору
- добавить функцию
rowRenderer
- добавить функцию
rowHeight
<Table
...
deferredMeasurementCache={rowCache}
rowRenderer={rowRenderer}
rowHeight={rowHeight}
>
Функции будут показаны позже. Table
ничего не сделает с deferredMeasurementCache
, кроме как передать его в качестве опоры Grid
Table
.
Для каждого Column
, которое необходимо измерить, добавьте функцию cellRenderer
. Во многих более простых случаях одну и ту же функцию можно использовать для всех измеряемых столбцов:
<Column
...
cellRenderer={measuredCellRenderer}
/>
Чтобы помочь координировать использование двух кешей, необходимы три дополнительных элемента данных:
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});
}
},
}
rowParent
используется, чтобы открыть сетку таблицы для rowRenderer. cellParent
служит посредником между двумя кэшами и между строкой, ее Column
ячейками и Table
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;
}
Обратите внимание, что существует два разных использования CellMeasurer
. Один находится внутри функции measuredCellRenderer
и использует cellCache
и cellParent
. Другой находится внутри функции rowRenderer
и использует rowCache
и rowParent
.
Кроме того, функция rowHeight
не просто сообщает о высоте строки. Он также отвечает за перенос rowHeight
строки в cellCache
на высоту ячейки строки для первого и единственного столбца в rowCache
.
Это решение можно несколько упростить, если в таблице есть только один измеряемый столбец. Нужен только один CellMeasurerCache
. Один кэш может выполнять роль как cellCache
, так и rowCache
. Как результат:
- Нет необходимости в
cellParent
; его можно удалить. Ссылки на cellParent
могут быть заменены ссылками на rowParent
, или, в случае функции measuredCellRenderer
, свойство CellMeasurer
parent
может быть установлено непосредственно на аргумент функции parent
.
- Внутри
measuredCellRenderer
CellMeasurer
необходимо жестко запрограммировать для columnIndex={0}
, даже если измеряемый столбец не является первым столбцом в таблице.
- Оператор
if
внутри функции rowHeight
может быть удален, поскольку нет необходимости передавать высоту между двумя кешами.
aMeasuredColumnIndex
можно удалить, так как он упоминается только в операторе rowHeight
if
.
person
sudr minz
schedule
17.07.2019
<CellMeasurer>
для обтекания<Column>
? - person Yixing Liu   schedule 13.08.2018