I'd like to echo what has been said here many times, thanks for a great set of components and for your dedication to helping people out with their problems. It took me a while to bend my brain around the infinite loading a list of items, but I've almost got there in the end.
One thing I am struggling with is a use case whereby I am loading up an infinite timeline. Each entry is of dynamic height which I won't know until the data is fetched and the component is rendered. I'm within spitting distance of a (probably bad) solution, but not quite there.
I'm doing this in the higher container's renderRow passed down into the list
<ReactHeight onHeightReady={height => this.heights[index] = height}>
{content}
</ReactHeight>
I'm also passing down a rowHeight function
rowHeight={({index}) => this.heights[index] || 60}
Problem is that this function gets called before loadMoreRows, therefore (obviously) there is no row rendered and no height to provide. I could live with that... it'll be 60 for empty and placeholder rows.
The more pressing issue though, is that after the row data does get returned and rendered on screen, the rowHeight callback isn't called. The rows stick at 60. That is, until I scroll the list a smidgen, then they all snap to their correct heights and it works fine from there on in.
Am I doing something wrong; is there a better way, or is it an issue with the InfiniteLoader/List?
Thanks for the kind words, @tomjmul. 馃槃
There are a couple of issues at play here:
Grid (the thing used inside of List) caches cell sizes once they've been measured. This is done because some measuring is expensive (eg measuring done by CellMeasurer) so it's faster to cache and reuse. Grid provides a method for clearing out cached sizes though, as does List- recomputeRowHeights. In your case, you'll want to call this method the first time a row (or a set of rows) is measured for real. If it's a single row, specify that row's index. If it's a set of rows, specify the first row's index (the lowest index).Grid temporarily caches inline style objects while a scroll is in progress to avoid causing shallow compare to fail and require the rows to be re-rendered. I _doubt_ this is impacting your use case, but even if it was- the recompute method mentioned previously will reset the style cache as well.I think this info should enable you to fix your problem, in which case- please close this issue. If not let's talk more. 馃槃
Ah yes, I had thought of using recomputeRowHeights, but I couldn't figure out a way to connect up the higher-level rendering function that knows when the component has actually been rendered and that the height is available for real, with the lower-level List's method.
Sorry if that isn't clear enough... I'm struggling here, if you can't already tell :smile: . Thanks for the help.
Here's a starter for you. It probably has bugs. I'm at work so I'm too busy to actually test it. 馃槃
class HowIImagineYourComponent extends Component {
constructor(props, context) {
super(props, context);
this._heights = {};
this._listRef = this._listRef.bind(this);
this._rowHeight = this._rowHeight.bind(this);
this._rowRenderer = this._rowRenderer.bind(this);
}
_listRef(ref) {
this._list = ref;
}
_rowHeight({index}) {
return this._heights.hasOwnProperty(index)
? this._heights[index]
: 60;
}
_rowRenderer({index, key, style}) {
const content; // You would set this somehow
if (this._heights.hasOwnProperty(index)) {
return (
<div
key={key}
style={style}
>
{content}
</div>
);
} else {
// Maybe you don't need the wrapper <div>; I don't konw ReactHeight.
return (
<div
key={key}
style={style}
>
<ReactHeight
key={key}
onHeightReady={(height) => {
this._heights[index] = height;
this._list.recomputeRowHeights(index);
}}
>
{content}
</ReactHeight>
</div>
);
}
}
render() {
return (
<List
{...yourListProps}
ref={this._listRef}
rowHeight={this._rowHeight}
rowRenderer={this._rowRenderer}
/>
);
}
}
That's really above and beyond. Many thanks!
It works too. I pass down a setter that pulls the List ref up into the parent, then use that ref to call recomputeRowHeights. Feels a bit dirty, but it works and that's the most important thing. What about the render function being passed a ref directly along with the index, style etc.? I don't know.
The list scrolls a fair bit slower, which is to be expected I suppose. One thing I don't understand though, is that the ref prop function in the List component gets called multiple times at the beginning, then during scrolling - always in sets of two, first with a null ref arg and then with the actual list ref. I would have expected this to only have been called once when the List is first rendered. Anyways, I'll try to figure that out separately.
Again, many thanks for your help on this.
What about the render function being passed a ref directly along with the index, style etc.?
I suppose that could be done, but the more standard React way of doing this would be with a ref setter prop. (I'd have to think more about potential downsides of passing ref to render function. Haven't thought about it before TBH. Seems like the sort of thing that may only be useful in an edge-case kind of way.)
One thing I don't understand though, is that the ref prop function in the List component gets called multiple times, always in sets of two, first with a null ref arg and then with the actual list ref. I would have expected this to only have been called once when the List is rendered. Anyways, I'll try to figure that out separately.
This is done by React during reconciliation. I understand why it would be a surprise- but it's working as expected. 馃榿
You're welcome!
Most helpful comment
Here's a starter for you. It probably has bugs. I'm at work so I'm too busy to actually test it. 馃槃