После некоторых проб и ошибок я нашел хороший способ сделать это - заставить функцию rowRenderer
решать, каким способом визуализировать строку. Вы можете сделать это, проверив свойство _rowHeightCache
в экземпляре CellMeasurerCache
use use в вашем списке. Обратите внимание, что ключи _rowHeightCache
принимают форму: «index
-0», где index
- это индекс строки.
Вот как вы можете настроить средство визуализации строки:
TextRowRenderer(
{
key, // Unique key within array of rows
index, // Index of row within collection
// isScrolling, // The List is currently being scrolled
style, // Style object to be applied to row (to position it)
parent, // reference to List
},
) {
const { textArray } = this.props;
// get the cached row height for this row
const rowCache = this.listCache._rowHeightCache[`${index}-0`];
// if it has been cached, render it using the normal, more expensive
// version of the component, without bothering to wrap it in a
// CellMeasurer (it's already been measured!)
// Note that listCache.defaultHeight has been set to 0, to make the
// the comparison easy
if (rowCache !== null && rowCache !== undefined && rowCache !== 0) {
return (
<TextChunk
key={key}
chunk={textArray[index]}
index={index}
style={style}
/>
);
}
// If the row height has not been cached (meaning it has not been
// measured, return the text chunk component, but this time:
// a) it's wrapped in CellMeasurer, which is configured with the
// the cache, and
// b) it receives the prop textOnly={true}, which it tells it to
// to skip rendering the markup
return (
<CellMeasurer
cache={this.listCache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}
>
{() => {
return (
<TextChunk
key={key}
chunk={textArray[index]}
index={index}
style={style}
textOnly
/>
);
}}
</CellMeasurer>
);
}
Затем он передается компоненту List
обычным способом:
<List
height={pageHeight}
width={pageWidth}
rowCount={textArray.length}
rowHeight={this.listCache.rowHeight}
rowRenderer={this.TextRowRenderer}
deferredMeasurementCache={this.listCache}
style={{ outline: 'none' }}
ref={(r) => { this.listRef = r; }}
/>
Включив обратный вызов ссылки, мы теперь можем получить доступ к методу measureAllRows
, который мы можем использовать для принудительного List
для отрисовки всех строк заранее, чтобы получить высоту. Это обеспечит правильную работу полосы прокрутки, но даже с флагом textOnly
может потребоваться больше времени. В моем случае, я считаю, что ожидание того стоит.
Вот как вы можете вызвать measureAllRows
:
componentDidUpdate() {
const {
textArray,
} = this.props;
// We will only run measureAllRows if they have not been
// measured yet. An easy way to check is to see if the
// last row is measured
const lastRowCache = this
.listCache
._rowHeightCache[`${textArray.length - 1}-0`];
if (this.listRef
|| lastRowCache === null
|| lastRowCache === undefined
|| lastRowCache === 0) {
try {
this.listRef.measureAllRows();
} catch {
console.log('failed to measure all rows');
}
}
}
Блок try-catch необходим, потому что если он пытается измерить перед построением List
он выдаст ошибку выхода индекса.
ЗАКЛЮЧИТЕЛЬНЫЕ МЫСЛИ
Другой возможный способ сделать это - иметь Фактический компонент List
зависит от кэшированных измерений, а не от функции rowRenderer
. Вы можете отобразить массив текста как обычный список, заключив каждую строку в <CellMeasurer>
. Когда кеш заполнен, вы затем визуализируете виртуальный List
, настроенный с использованием предварительно заполненного кеша. Это избавит от необходимости вызывать measureAllRows
в componentDidUpdate
или обратный вызов useEffect
.