【问题标题】:Is there a way to implement grid header with dynamic height using react-virtualized?有没有办法使用反应虚拟化实现具有动态高度的网格标题?
【发布时间】:2018-05-09 09:33:20
【问题描述】:

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

问题是单元格大小是在渲染单元格后计算的(afaik),所以如果高度已更改,我必须在标题的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 实现具有动态高度的网格标题?

【问题讨论】:

    标签: reactjs react-virtualized


    【解决方案1】:

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

    如果单元格的高度发生变化(例如从rowHeight 返回的值),则CellMeasurer will automatically notify the parent Grid。在这种情况下,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,
            });
          }
        }
      }
    }
    

    【讨论】:

    • 布莱恩,感谢您的支持和您所做的工作!至于答案,这与我试图实现的目标有点不同,虽然帮助很大。
    【解决方案2】:

    我觉得我必须将其发布为答案。

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

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

    _onSectionRendered = ({ rowOverscanStartIndex }) => {
        if (rowOverscanStartIndex === 0) {
          const height = this._cache.rowHeight({ index: 0 });
    
          if (height !== this.state.headerHeight) {
            this.setState({
              headerHeight: height,
            });
          }
        }
      }
    

    来自原始代码框的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,
          });
        }
      }
    

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

    虽然它不能避免额外的渲染,但它比超时黑客要干净得多。 完整示例在这里:https://codesandbox.io/s/kor4vv6jqv

    【讨论】:

      猜你喜欢
      • 2018-01-18
      • 2017-08-20
      • 2019-06-12
      • 2017-10-05
      • 1970-01-01
      • 2016-07-08
      • 2018-01-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多