1
votes

I am trying to create a chat application using React Virtualized. So far everything is working but I think think I am doing something wrong when using keyMapper and rowRenderer from the list.

The cache is using the id to store the height in the _rowHeightCache but it seems that the heights are looked up using the index and not the id. Im not sure if I should be passing the id as the rowIndex to CellMeasurer to get the right heights or something of that nature.

The issue is that the heights are wrong due to the changing order of the messages list so the index's don't have the proper heights and also that messages can be removed messing up the index orders. This I think should be fixed by using keyMapper to look up the heights but i must be doing it wrong.

The following is an edited down version of how I am using it.

constructor(props) {
        this._cache = new CellMeasurerCache({
            defaultHeight: 45,
            fixedWidth: true,
            keyMapper: (index) => _.get(this.props.messageList[index], ['msgId'])
        });
    }

    render() {
        const props = this.filterProps(this.props),
            list = this.props.messageList,
            rowCount = list.length;

        return (

            <InfiniteLoader
                threshold={0}
                isRowLoaded={this._isRowLoaded}
                loadMoreRows={this._loadMoreRows}
                rowCount={rowCount}>
                {({onRowsRendered, registerChild}) => (
                    <AutoSizer>
                        {({width, height}) => (
                            <List
                                ref={(ref) => {
                                    this.streamLane = ref;
                                    registerChild(ref);
                                }}
                                height={height}
                                width={width}
                                onRowsRendered={onRowsRendered}
                                rowCount={rowCount}
                                rowRenderer={this._rowRenderer}
                                deferredMeasurementCache={this._cache}
                                rowHeight={this._cache.rowHeight}
                            />
                        )}
                    </AutoSizer>
                )}
            </InfiniteLoader>
       );
   }

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

    _loadMoreRows({startIndex, stopIndex}) {

        return new Promise((resolve) => {
            if (!!stopIndex || !!startIndex || this.props.hasLastMessage || this.state.isPinned) {
                return resolve();
            }

            this.props.loadOlder(() => resolve);
        });
    }

    _isRowLoaded({index}) {
        if (index === 0) return false;
        return !!this.props.messageList[index];
    }
  }

Any help, suggestions or criticisms would be amazing.

1
@brianvaughn sorry to bother you someone mentioned it might be worth tagging you to this post. No worries if you don't have time. Thanks! - ReganPerkins

1 Answers

0
votes

The issue is that the heights are wrong due to the changing order of the messages list so the index's don't have the proper heights and also that messages can be removed messing up the index orders.

Your example code doesn't show how the order changes or what you do after it changes, so it's hard to say what you're doing wrong.

But when the order changes (eg you sort the data, or you add new items to the beginning of the list causing older items to shift) then you'll need to let List know that its cached positions may be stale. Do do this in your case, you'd need to call this.streamLane.recomputeRowHeights().

(You can pass the index of the first changed item to recomputeRowHeights but I assume in your case that maybe all items have shifted? I don't know, since I don't have all of your code.)

After you call recomputeRowHeights, List will re-calculate the layout using the heights reported to it by CellMeasurerCache. That cache uses your keyMapper function:

rowHeight = ({index}: IndexParam) => {
  const key = this._keyMapper(index, 0);

  return this._rowHeightCache.hasOwnProperty(key)
    ? this._rowHeightCache[key]
    : this._defaultHeight;
};

So it won't need to remeasure items, even though their indices have changed.