有没有办法使用 react-virtualized 实现具有动态高度的网格 header?

Is there a way to implement grid header with dynamic height using react-virtualized?

我正在开发一个基于 react-virtualized 的数据网格组件。它应该有一个固定的 header 和可调整大小的列。我希望 header 根据 header 单元格内容更改其高度。我正在使用 CellMeasurer 来计算 header 的单元格高度和更新高度。

问题是单元格大小是在渲染单元格后计算的 (afaik),所以如果高度有,我必须在 header 的 render 内部调用 forceUpdate已更改。

这里是 render 的样子(完整的例子是 here):

render() {
    const height = this._cache.rowHeight({ index: 0 });
    console.log('render', height, this.props.sizes);
    setTimeout(() => {
      if (height !== this._cache.rowHeight({ index: 0 })) {
        console.log('forceUpdate', this._cache.rowHeight({ index: 0 }))
        this.forceUpdate()
      }
    }, 0)

    return (
      <Grid 
        ref={ref => this._grid = ref}
        deferredMeasurementCache={this._cache}
        width={1500}
        height={height}
        columnCount={5}
        columnWidth={this.columnWidth}
        rowCount={1}
        rowHeight={this._cache.rowHeight}
        cellRenderer={this.renderCell}
      />
    );
  }

所以问题是如何避免 forceUpdate?有没有更简洁的方法来使用 react-virtualized 实现具有动态高度的网格 header?

render 中设置超时可能不是一个好主意。

如果单元格的高度发生变化(例如 rowHeight 返回的值),则 CellMeasurer will automatically notify the parent Grid. In that case, Grid will automatically clear its position cache and re-render.

在此示例中,您所做的不同之处在于将 Grid 的外部高度设置为等于单元格测量的高度。这不是一个常见的用例,但是你可以在没有定时器黑客的情况下支持它,就像这样:

class XHeader extends React.Component {
  _cache = new CellMeasurerCache({
    defaultHeight: 20,
    fixedWidth: true,
  });
  state = {
    headerHeight: 20,
  };
  renderCell = props => {
    return (
      <HeaderCell 
        width={this.props.sizes[props.columnIndex]}
        {...props}
        cache={this._cache}
        onMouseDown={this.props.onMouseDown}
      />
    );
  }
  columnWidth = ({ index }) => {
    return this.props.sizes[index];
  }
  render() {
    return (
      <Grid 
        ref={ref => this._grid = ref}
        deferredMeasurementCache={this._cache}
        width={1500}
        height={this.state.headerHeight}
        columnCount={5}
        columnWidth={this.columnWidth}
        onSectionRendered={this._onSectionRendered}
        rowCount={1}
        rowHeight={this._cache.rowHeight}
        cellRenderer={this.renderCell}
      />
    );
  }

  _onSectionRendered = ({ rowOverscanStartIndex }) => {
    if (rowOverscanStartIndex === 0) {
      const height = this._cache.rowHeight({ index: 0 });

      if (height !== this.state.headerHeight) {
        this.setState({
          headerHeight: height,
        });
      }
    }
  }
}

我觉得我必须post它作为答案。

Brian 指出了正确的方向,感谢 Brian 的出色工作和支持。

那个 onSectionRendered 处理程序很重要,因为它让我们在第一次渲染后更新 header 的高度:

_onSectionRendered = ({ rowOverscanStartIndex }) => {
    if (rowOverscanStartIndex === 0) {
      const height = this._cache.rowHeight({ index: 0 });

      if (height !== this.state.headerHeight) {
        this.setState({
          headerHeight: height,
        });
      }
    }
  }
来自原始 codesandbox 的

componentWillReceiveProps 也很重要——它使组件对列大小调整做出反应:

componentWillReceiveProps(nextProps) {
    if (nextProps.sizes !== this.props.sizes) {
      this._cache.clearAll();
      console.log('recomputeGridSize');
      this._grid.recomputeGridSize();
      console.log('===', this._cache.rowHeight({ index: 0 }))
    }
  }

缺少的是componentDidUpdate:

componentDidUpdate() {
    console.log('componentDidUpdate',
      this._cache.rowHeight({ index: 0 }));
    const height = this._cache.rowHeight({ index: 0 });

    if (height !== this.state.headerHeight) {
      this.setState({
        headerHeight: height,
      });
    }
  }

-- 这是我们可以检查行的高度是否不同于 header 的高度并在必要时更新它的方法。 componentDidUpdate 不会在初始渲染后调用,这就是 onSectionRendered 有用的原因。

虽然它没有避免额外的渲染,但它比超时 hack 更干净。 完整的例子在这里:https://codesandbox.io/s/kor4vv6jqv