Перекрытие элементов при использовании списка динамической высоты

У меня почти такая же проблема, как у Facebook при рендеринге фида. Как только отрисовывается много постов, появляются проблемы с производительностью. После некоторых исследований я обнаружил, что react-virtualized — это круто, но у меня возникли проблемы с тем, чтобы заставить его работать для моего приложения.

Вот проблемы, с которыми я сталкиваюсь:

  1. Поскольку в каждое сообщение может быть встроен iframe или изображение, я запускаю обратный вызов measure из CellMeasurer после загрузки этих элементов. Из-за этого некоторые элементы кажутся смещенными. Я пробовал делать parent.measureAllRows() и parent.recomputeRowHeights() каждый раз, когда вызывается обратный вызов measure, чтобы посмотреть, исправит ли он это, но это не так.
  2. Каждый пост имеет расширяемый раздел, поэтому мне нужно пересчитать его высоту. Есть ли альтернатива, кроме отправки реквизита компоненту?

Это установка:

class VirtualPostList extends React.PureComponent {
  constructor(props, context) {
    super(props, context);
    this._cache = new ReactVirtualized.CellMeasurerCache({
      fixedWidth: true,
      defaultHeight: 400
    });
    this.rowRenderer = this.rowRenderer.bind(this);
  }


  rowRenderer({index, isScrolling, key, parent, style}) {
    return (
      <ReactVirtualized.CellMeasurer
        cache={this._cache}
        columnIndex={0}
        key={key}
        parent={parent}
        rowIndex={index}>
        {({measure}) => (
          <div key={key} style={style}>
            <Post onLoad={measure}/>
          </div>
        )}
      </ReactVirtualized.CellMeasurer>
    )
  }

  componentDidUpdate(){
    this.listComponent.recomputeRowHeights();
  }

  render() {
    const cache = this._cache;
    const rowCount = this.props.posts.length;
    const _this = this;
    return (
      <ReactVirtualized.WindowScroller>
        {({height, isScrolling, onChildScroll, scrollTop}) =>
          <ReactVirtualized.AutoSizer disableHeight>
            {({width}) =>
              <ReactVirtualized.List
                autoHeight
                isScrolling={isScrolling}
                height={height}
                width={width}
                rowCount={rowCount}
                deferredMeasurementCache={cache}
                rowHeight={cache.rowHeight}
                rowRenderer={this.rowRenderer}
                scrollTop={scrollTop}
                onScroll={onChildScroll}
                ref={(listComponent) => _this.listComponent = listComponent}
              />
            }
          </ReactVirtualized.AutoSizer>}
      </ReactVirtualized.WindowScroller>
    )
  }
}

Пример перекрытия:

введите здесь описание изображения


person Matías Hernán García    schedule 27.07.2017    source источник
comment
Вы уверены, что ваш обратный вызов onLoad не срабатывает слишком рано? Я был бы рад взглянуть на Plnkr, если вы создадите реплику. Это должно работать нормально. Я предполагаю, что есть какая-то проблема со временем.   -  person bvaughn    schedule 28.07.2017
comment
Спасибо за ответ мужик. Ваша библиотека великолепна. Я понял, что не запускаю обратный вызов onLoad при загрузке маленьких аватаров. Может быть, это оно. Хотя не уверен.   -  person Matías Hernán García    schedule 28.07.2017
comment
Это может быть связано с этим, но я заметил, что производительность при этом значительно снижается. Есть ли простой способ заменить текущий CellMeasurer таким компонентом, как ReactMeasure? Я нашел ReactMeasure проще, так как он использует наблюдатель изменения размера. Я видел, что вы прокомментировали что-то об этом в репозитории github.   -  person Matías Hernán García    schedule 28.07.2017
comment
Пожалуйста. :) Насколько я знаю, CellMeasurer должен быть довольно оптимизирован для работы с Grid. Не уверен, что могло бы замедлить работу, если бы вы активировали обратный вызов в нужное время. На виртуализированной демо-странице реакции я загружаю изображения в List с CellMeasurer и использую onLoad для их измерения аналогичным образом, и, похоже, это работает довольно хорошо.   -  person bvaughn    schedule 29.07.2017
comment
Спасибо за ответ еще раз!. Я мог бы попытаться решить проблему с производительностью, но я заметил, что прокрутка медленная, даже когда присутствуют 5 элементов. Наверное, что-то с моей стороны. Я знаю, что компоненты, которые я использую, довольно тяжелые. Однако мои компоненты могут расширяться, поэтому у меня возникла проблема с их измерением. Есть ли шаблон, которому я могу следовать, кроме запуска обратного вызова измерения каждый раз? Вот почему я упомянул ReactMeasure, который прослушивает событие изменения размера элемента, и поэтому вызовы для измерения проще.   -  person Matías Hernán García    schedule 30.07.2017
comment
Хорошо, вместо того, чтобы вручную вызывать измерение в каждом месте, которое мне нужно, я прикрепил наблюдатель изменения размера к div, который использует стиль CellMeasurer. Каждый раз, когда происходит изменение размера, выполняется вызов функции measure. Я прямо сейчас профилирую его, кажется, что он немного менее эффективен, чем другой вариант, но на данный момент избавляет от многих головных болей, и этого достаточно для моих требований :)   -  person Matías Hernán García    schedule 30.07.2017


Ответы (1)


Как предложил @brianvaughn, я не вызывал метод measure везде, где должен. Они стали немного шаблонными и трудными в обслуживании, поскольку элементы не только имеют изображения, но и могут расширяться или сжиматься.

Так как я хотел избежать ручного вызова measure, я присоединил ResizeObserver к компоненту так же, как это делает ReactMeasure. После этого я изменил функцию rowRenderer на эту:

  rowRenderer({index, isScrolling, key, parent, style}) {
    return (
      <ReactVirtualized.CellMeasurer
        cache={this._cache}
        columnIndex={0}
        key={key}
        parent={parent}
        rowIndex={index}>
        {({measure}) => (
          <div key={key} style={style}>
            <ResizeObservable onResize={measure}>
              <Post/>
            </ResizeObservable>
          </div>
        )}
      </ReactVirtualized.CellMeasurer>
    )
  }

Как видите, функция измерения вызывается каждый раз, когда происходит изменение размера. Это также включает в себя загрузку изображений или фреймов, поэтому нет необходимости вызывать их вручную.

person Matías Hernán García    schedule 29.07.2017
comment
Очень здорово видеть, как виртуализированный реагирование и наблюдатель изменения размера используются вместе вот так. - person bvaughn; 30.07.2017
comment
Не могли бы вы поделиться, откуда взялся этот компонент ResizeObservable? я не могу найти его нигде в Интернете. - person YTG; 22.04.2021
comment
@YTG npmjs.com/package/rc-resize-observer - person asmaa; 18.05.2021