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!
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.