Reputation: 241
I have almost the same problem as Facebook has when rendering it's feed. As soon as a lot of posts are rendered, performance problems appear. After some research I found react-virtualized, which is awesome but I am having trouble to make it work for my app.
These are the problems that I am facing:
measure
callback from the CellMeasurer
once these items are loaded. Because of this, some items seem misaligned. I tried doing parent.measureAllRows()
and parent.recomputeRowHeights()
each time the measure
callback is called to see if it would fix it but it doesn't.This is the setup:
class VirtualPostList extends React.PureComponent {
constructor(props, context) {
super(props, context);
this._cache = new ReactVirtualized.CellMeasurerCache({
fixedWidth: true,
defaultHeight: 400
});
this.rowRenderer = this.rowRenderer.bind(this);
}
rowRenderer({index, isScrolling, key, parent, style}) {
return (
<ReactVirtualized.CellMeasurer
cache={this._cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}>
{({measure}) => (
<div key={key} style={style}>
<Post onLoad={measure}/>
</div>
)}
</ReactVirtualized.CellMeasurer>
)
}
componentDidUpdate(){
this.listComponent.recomputeRowHeights();
}
render() {
const cache = this._cache;
const rowCount = this.props.posts.length;
const _this = this;
return (
<ReactVirtualized.WindowScroller>
{({height, isScrolling, onChildScroll, scrollTop}) =>
<ReactVirtualized.AutoSizer disableHeight>
{({width}) =>
<ReactVirtualized.List
autoHeight
isScrolling={isScrolling}
height={height}
width={width}
rowCount={rowCount}
deferredMeasurementCache={cache}
rowHeight={cache.rowHeight}
rowRenderer={this.rowRenderer}
scrollTop={scrollTop}
onScroll={onChildScroll}
ref={(listComponent) => _this.listComponent = listComponent}
/>
}
</ReactVirtualized.AutoSizer>}
</ReactVirtualized.WindowScroller>
)
}
}
Example of overlap:
Upvotes: 3
Views: 3160
Reputation: 241
As @brianvaughn suggested, I wasn't calling the measure
method in every place I should. These became a bit boilerplate and hard to mantain since elements not only have images but they can expand or contract themselves.
Since I wanted to avoid manually calling measure
, I attached a ResizeObserver to the component just like ReactMeasure
does. After that, I changed the rowRenderer
function to this:
rowRenderer({index, isScrolling, key, parent, style}) {
return (
<ReactVirtualized.CellMeasurer
cache={this._cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}>
{({measure}) => (
<div key={key} style={style}>
<ResizeObservable onResize={measure}>
<Post/>
</ResizeObservable>
</div>
)}
</ReactVirtualized.CellMeasurer>
)
}
As you see, the measure function is called each time a resize happens. This also includes when images or iframes are loaded so no need to manually call them
Upvotes: 1