使用动态高度List时元素重叠

Overlap of elements when using dynamic height List

我在呈现 Feed 时遇到了与 Facebook 几乎相同的问题。一渲染很多post,性能问题就出现了。经过一些研究后,我发现了 react-virtualized,这很棒,但我很难让它适用于我的应用程序。

这些是我面临的问题:

  1. 由于每个 post 都可以嵌入 iframe 或图像,所以一旦这些项目加载完毕,我就会从 CellMeasurer 触发 measure 回调。因此,有些项目似乎错位。每次调用 measure 回调时,我都尝试执行 parent.measureAllRows()parent.recomputeRowHeights() 以查看它是否会修复它,但它没有。
  2. 每个 post 都有一个可展开的部分,因此我需要重新计算它的高度。除了将道具发送到组件之外,还有其他选择吗?

这是设置:

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>
    )
  }
}

重叠示例:

正如@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>
    )
  }

如您所见,每次调整大小时都会调用测量函数。这还包括何时加载图像或 iframe,因此无需手动调用它们