Search code examples
javascriptreactjsreact-nativereact-virtualized

React-virtualized dynamic height list renders everything stacked initially


I am trying to use react-virtualized to virtualize a list where some rows have varying heights, and also the list takes up all the space in the parent. I am trying to accomplish this with the CellMeasurer, AutoSizer and List components.

My package versions are as follows:

react: "16.8.6"
react-dom: "16.8.6"
react-virtualized: "^9.21.1"
import React, { PureComponent } from 'react';
import 'react-virtualized/styles.css';
import AutoSizer from 'react-virtualized/dist/commonjs/AutoSizer';
import { CellMeasurer, CellMeasurerCache, List } from 'react-virtualized';


class Table extends PureComponent {

  rowRenderer = ({ index, style, key }) => {
    return (
      <CellMeasurer
        cache={this.cache}
        columnIndex={0}
        key={key}
        parent={parent}
        rowIndex={index}
      >
        <div style={style} key={key}>
          content
        </div>
      </CellMeasurer>
    );
  }

  cache = new CellMeasurerCache({
    defaultHeight: 24,
    fixedWidth: true,
  });

  renderAutoSizerContent = () => {
    return this.RenderList;
  }

  RenderList = ({ height, width }) => {
    return (<List
      items={this.props.items}
      width={width}
      height={height}
      rowCount={this.props.items.length}
      rowHeight={this.cache.rowHeight}
      rowRenderer={this.rowRenderer}
      deferredMeasurementCache={this.cache}
    />
    );
  }


  render() {
    return (
      <AutoSizer
        items={ this.props.items}
      >
        {this.renderAutoSizerContent()}
      </AutoSizer>
    );
  }
}

export default Table;

After the initial render everything looks like this. For some reason the top attribute is 0 for every element in the array:

enter image description here

After scrolling or triggering a rerender the items seem to get their top property and the following renders. In the actual code some of my elements have height variance, but the height seems to default to the defaultHeight I give to the CellMeasurerCache constructor anyway.

After scrolling once the list seems to render, but still some of the heights are still wrong

How do I get the initial render to have top property for each element, and how do I get the heights to calculate correctly? What am I doing wrong in the code I have shown here?


Solution

  • In your rowRenderer component, you provided parent prop to CellMeasurer but parent is undefined.
    You get parent from rowRenderer as written in the docs: https://github.com/bvaughn/react-virtualized/blob/master/docs/List.md#rowrenderer


    So your rowRenderer component should be:

      rowRenderer = ({ index, style, key, parent }) => {
          return (
            <CellMeasurer
                cache={this.cache}
                columnIndex={0}
                key={key}
                parent={parent}
                rowIndex={index}
            >
               <div style={style} key={key}>
                  {items[index]}
              </div>
            </CellMeasurer>
           );
      }
    

    You can also check this code sandbox with working example: https://codesandbox.io/s/material-demo-yw1k8?fontsize=14