Как сообщить компоненту Masonry, что его ячейки изменили свою высоту?

У меня есть компонент реакции, который отображает одномерный список элементов в виде сетки. Все элементы имеют одинаковую ширину и высоту и расположены в трех столбцах. Их может быть много, поэтому я решил дать волю React-Virtualized, используя компонент Masonry, который, кажется, был написан именно для этого варианта использования. Это работает хорошо, но с одной оговоркой: если элементы изменяют свою высоту, я не могу заставить Masonry это заметить.

Вот уменьшенный пример:

constructor(props) {
    [...]
    this.cellMeasurerCache = new CellMeasurerCache({
        defaultHeight: this.state.height,
        fixedWidth: true,
    });

    this.cellPositioner = createMasonryCellPositioner({
        cellMeasurerCache: this.cellMeasurerCache,
        columnCount: 3,
        columnWidth: 250,
        spacer: 20,
    });
}

[...]

cellRenderer({ index, key, parent, style }) {
    return (
        <CellMeasurer
            cache={this.cellMeasurerCache}
            index={index}
            key={key}
            parent={parent}
        >
            <div style={style}>
                [...]
            </div>
        </CellMeasurer>
    );
}

resetMasonry() {
    this.cellMeasurerCache.clearAll();

    this.cellPositioner.reset({
        cache: this.cellMeasurerCache,
        columnCount: 3,
        columnWidth: 250,
        spacer: 20,
    });

    this.masonryRef.recomputeCellPositions();
}

render() {
    return (
        <AutoSizer>
            {({ height, width }) =>
                <Masonry
                    cellCount={this.state.list.length}
                    cellMeasurerCache={this.cellMeasurerCache}
                    cellPositioner={this.cellPositioner}
                    cellRenderer={this.cellRenderer}
                    height={height}
                    ref={this.setMasonryRef}
                    width={width}
                />
            }
        </AutoSizer>
    );
}

resetMasonry вызывается, когда состояние высоты компонента изменилось. Я культивировал текущий код из нескольких ответов stackoverflow и других ресурсов, даже просмотрел исходный код, но ничего не работает. Я заметил, что я не сообщаю cellMeasurerCache или чему-либо еще об изменении высоты, поэтому я действительно не должен ожидать, что это сработает, но, похоже, нет никакого способа получить эту информацию там, даже путем создания экземпляра новый CellMeasurerCache.

Между прочим, если я изменю columnWidth в пределах cellPositioner.reset, компонент Masonry обновится соответствующим образом, конечно же.

Кто-нибудь знает, чего мне не хватает, чтобы заставить его работать для изменения высоты? Спасибо!


person Martin Denk    schedule 13.06.2017    source источник


Ответы (1)


Если размеры вашей ячейки Masonry изменились, вам необходимо очистить кешированные размеры в вашей CellMeasurerCache, а затем убедиться, что cellPositioner также знает о новых размерах. Например:

// Clear cached measurements since sizes have changed:
cellMeasurerCache.clearAll()

// Let your component know it needs to re-render
this.setState({...}, () => {
  // Assuming you're using the default createCellPositioner()
  // Let it know of any values that may have changed:
  cellPositioner.reset({
    columnCount,
    columnWidth,
    spacer
  })

  // Tell Masonry to discard any cached position data:
  masonryRef.clearCellPositions()
})

Вы можете увидеть демонстрацию компонента Masonry с изменением высоты столбцов на сайте RV просто изменив ширину столбца. Исходный код этой демонстрации доступен на GitHub. репо также.

Однако из того, что вы описываете, я бы не советовал использовать Masonry. На самом деле я бы просто предложил использовать List аналогично тому, как я сделал в этом примере: http://plnkr.co/edit/zjCwNeRZ7XtmFp1PDBsc?p=preview

Ключевым битом является динамический расчет количества элементов в строке на основе доступной ширины, а затем вы даете List скорректированное rowCount:

  const itemsPerRow = Math.floor(width / ITEM_SIZE);
  const rowCount = Math.ceil(ITEMS_COUNT / itemsPerRow);
person bvaughn    schedule 13.06.2017
comment
Большое спасибо за ответ! Использование List оказалось правильным выбором (я уже знал, как его использовать, потому что сделал это в другом компоненте, который отображал длинный список элементов полной ширины). Количество столбцов в моем варианте использования никогда не меняется, поэтому было достаточно легко адаптироваться. Теперь мне интересно, для каких вариантов использования предназначен компонент Masonry, если не для этого? Кроме того, оказалось, что мне не хватало использования clearCellPositions, а не recomputeCellPositions. Это заставило компонент заметить изменение высоты, но он обновил только ряд своих элементов, а не все... - person Martin Denk; 14.06.2017
comment
В любом случае, использование простого List работает очень хорошо, так что еще раз спасибо! - person Martin Denk; 14.06.2017
comment
Masonry предназначен для макетов Pinterest. На самом деле я создал компонент специально для Pinterest после разговора об этом с одним из их инженеров. (Не уверен, решили они его использовать или нет.) Поскольку высота ваших предметов одинакова, List проще использовать. :) - person bvaughn; 14.06.2017