11

I have gone through many answers on StackOverflow. I have also gone through List document here, react-virtualized/List. But, still I am not able to understand how to dynamically set row height in react-virtualized List. How to calculate the height in rowHeight prop function?

I can call my function like rowHeight={({ index }) => this.computeRowHeight({ index })}. But how will the function compute the row height?

Following is the code for reference.

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { AutoSizer, InfiniteLoader, List } from 'react-virtualized';
import _ from 'lodash';

class InfiniteList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      list: props.list,
      loading: false
    };
  }
  componentDidMount() {
    fetch('http://jsonplaceholder.typicode.com/comments')
      .then((response) => {
        response.json().then((data) => {
          this.setState({
            list: _.concat(this.state.list, _.map(data, 'body')),
            loading: false
          });
        });
      });
  }
  isRowLoaded({ index }) {
    return !!this.state.list[index];
  }

  loadMoreRows({ startIndex, stopIndex }) {
    if (this.state.loading) {
      return;
    }
    this.setState({
      loading: true
    });
    return fetch('http://jsonplaceholder.typicode.com/comments')
      .then((response) => {
        response.json().then((data) => {
          // Simulate delay
          setTimeout(() => {
            this.setState({
              list: _.concat(this.state.list, _.map(data, 'body')),
              loading: false
            });
          }, 3000);
        });
      });
  }

  rowRenderer({ key, index, isScrolling, isVisible, style }) {
    if (isVisible) {
      return (
        <div key={key}>
          <div style={style}>#{this.state.list[index]}.</div>
        </div>
      );
    }
  }

  render() {
    return (
      <div>
        <InfiniteLoader
          isRowLoaded={({ index }) => this.isRowLoaded({ index })}
          loadMoreRows={({ startIndex, stopIndex }) => this.loadMoreRows({ startIndex, stopIndex })}
          rowCount={this.state.list.length}
        >
          {({ onRowsRendered, registerChild }) => (
            <AutoSizer disableHeight>
              {({ width }) => (
                <List
                  onRowsRendered={onRowsRendered}
                  ref={registerChild}
                  width={width}
                  height={320}
                  rowCount={this.state.list.length}
                  rowHeight={40}
                  rowRenderer={({ key, index, isScrolling, isVisible, style }) => this.rowRenderer({ key, index, isScrolling, isVisible, style })}
                />
              )}
            </AutoSizer>
          )}
        </InfiniteLoader>
        {this.state.loading && <p>Loading...</p>}
      </div>
    );
  }
}

const list = [];

ReactDOM.render(
  <InfiniteList list={list} />,
  document.querySelector('#root')
);

Update

Dynamic height is now working with the following code with CellMeasurer. But, unfortunately this.loadMoreRows() function is not called in InfiniteLoader. Without CellMeasurer also it is not working. I am not sure what I did wrong in the following code.

import React, { Component } from 'react';
import { AutoSizer, CellMeasurer, InfiniteLoader, List } from 'react-virtualized';
import _ from 'lodash';

class InfiniteList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      list: props.list,
      loading: false
    };
  }
  componentDidMount() {
    fetch('http://jsonplaceholder.typicode.com/comments')
      .then((response) => {
        response.json().then((data) => {
          this.setState({
            list: _.concat(this.state.list, _.map(data, 'body')),
            loading: false
          });
        });
      });
  }
  isRowLoaded({ index }) {
    return !!this.state.list[index];
  }

  loadMoreRows({ startIndex, stopIndex }) {
    if (this.state.loading) {
      return;
    }
    this.setState({
      loading: true
    });
    return fetch('http://jsonplaceholder.typicode.com/comments')
      .then((response) => {
        response.json().then((data) => {
          // Simulate delay
          setTimeout(() => {
            this.setState({
              list: _.concat(this.state.list, _.map(data, 'body')),
              loading: false
            });
          }, 3000);
        });
      });
  }

  rowRenderer({ key, index, isScrolling, isVisible, style }) {
    if (isVisible) {
      return (
        <div key={key} style={style}>#{index} {this.state.list[index]}.</div>
      );
    }
  }
  cellRenderer({ columnIndex, key, rowIndex, style }) {
    return (
      <div
        key={key}
        style={style}
      >
        <div>#{rowIndex} {this.state.list[rowIndex]}.</div>
      </div>
    );
  }
  render() {
    return (
      <div>
        <InfiniteLoader
          isRowLoaded={isRowLoaded => this.isRowLoaded(isRowLoaded)}
          loadMoreRows={loadMoreRows => this.loadMoreRows(loadMoreRows)}
          rowCount={this.state.list.length}
        >
          {({ onRowsRendered, registerChild }) => (
            <AutoSizer disableHeight>
              {({ width }) => (
                <CellMeasurer
                  cellRenderer={cellRenderer => this.cellRenderer(cellRenderer)}
                  columnCount={1}
                  rowCount={this.state.list.length}
                >
                  {({ getRowHeight }) => (
                    <List
                      onRowsRendered={onRowsRendered}
                      ref={registerChild}
                      width={width}
                      height={400}
                      rowCount={this.state.list.length}
                      rowHeight={getRowHeight}
                      rowRenderer={rowRenderer => this.rowRenderer(rowRenderer)}
                    />
                  )}
                </CellMeasurer>
              )}
            </AutoSizer>
          )}
        </InfiniteLoader>
        {this.state.loading && <p>Loading...</p>}
      </div>
    );
  }
}

const list = [];

ReactDOM.render(
  <InfiniteList list={list} />,
  document.querySelector('#root')
);

Any help will be appreciated. Thanks!

krishnaxv
  • 956
  • 2
  • 6
  • 18

2 Answers2

1

You need to write computeRowHeight yourself to determine the height of the row. In some instances, based on the index and the properties of that row you might be able to know what the row's height will be. For example, if your rows might be different components, if you know a certain row will be component A with height 40 and another row will be component B with height 60, you can check those props and return the right height.

If you don't know the heights, then you're going to have to actually look at the element in the DOM and figure out it's height. If it doesn't yet exist in the DOM, you'll need to return a best guess initially and then call recomputeRowHeights after render and inspect the DOM then.

Drew Schuster
  • 2,521
  • 12
  • 15
  • 2
    Adding onto Drew's answer, you might want to use another react-virtualized component like CellMeasurer if you don't know anything about the size of your content. I have also heard of people using react-measure with react-virtualized with good results. – bvaughn Dec 31 '16 at 00:06
  • @brianvaughn Thanks! Previously I was not able to get dynamic height with CellMeasurer. But, I again gave it a try and now it is working (Although sometimes few rows overlap). But, unfortunately my `this.loadMoreRows()` function is not getting called in InfiniteLoader. I think it was also not working before CellMeasurer as well. So it has nothing to do with CellMeasurer. I am not sure what I am doing wrong here. Thanks for your time! – krishnaxv Dec 31 '16 at 13:26
  • I figured this out. I have to give arbitrary high number in `rowCount` in `InfiniteLoader` instead of `rowCount={this.state.list.length}`. – krishnaxv Dec 31 '16 at 23:23
  • 2
    anyone happen to have an example laying around a few years later? – lahwran Aug 10 '18 at 00:40
1

I went through all the things you and other people mentioned, and finally, I ended up creating this Example which I explained here too. It is using Table beside react hooks which may be helpful to some other people too. I build this one for some table which may have some expandable rows.

Naeem Baghi
  • 811
  • 2
  • 14
  • 29