react-virtualized
react-virtualized copied to clipboard
Preserve list position on changed row content
Feature Request
Struggle to make virtualized list scrolled to a particular row after content refresh. Scenario: there are few rows, user scrolls to somewhere in the list, and then triggers an event (e.g. presses a button in the app) that modifies the content of the list items. Ideally the user must see the same row at the top of the scroll view that was before the event occurred. See details below.
I could come up with two options:
1 recomputeRowHeights()
should have a parameter that allows preserving view position during computations.
2. list
should have some kind of callback where client can get notified on complete rendering rows (onRowRenders
gets called multiple time during remeasuring, so it does not suits). There must be correct method scrollToRow
, the current one is flaky in case of measurements must be done, and triggers multiple row sections re-rendered, i.e. moves the view from the specified row.
Here is the snippet from my code where I try to make it work with no success.
class MyList extends React.Component {
state = {
newScrollToIndex: undefined
}
// function triggered from outsided events
onCellContentChanged() {
const index = this.listRowIndex
// Don't know really which one to call exactlty
// Just called everything
this.cellMeasurerCache.clearAll()
this.listView.recomputeRowHeights()
this.listView.measureAllRows()
/*
* Tried this did not work at all.
* const off = this.listView.getOffsetForRow({ index })
* this.listView.scrollToPosition(off)
*/
// This two seem to work equivalently with 50% chance to work correctly.
// this.listView.scrollToRow(index)
this.setState({ newScrollToIndex: index })
}
renderInfiniteList({ height, width }) {
return (
<InfiniteLoader
isRowLoaded={this.isRowLoaded}
loadMoreRows={this.loadMoreRows}
rowCount={this.rowCount}
>
{({ onRowsRendered, registerChild }) => {
return (
<List
style={{ outline: 'none' }}
noRowsRenderer={() => (
<NoRows
loading={
this.state.loadingMoreRows || this.state.loadingFields
}
/>
)}
height={height}
width={width}
overscanRowCount={2}
rowCount={this.listViewRowCount}
rowHeight={this.cellMeasurerCache.rowHeight}
deferredMeasurementCache={this.cellMeasurerCache}
rowRenderer={this.rowRenderer}
scrollToIndex={this.state.newScrollToIndex}
onRowsRendered={(o) => {
onRowsRendered(o)
this.listRowIndex = o.startIndex
}}
ref={(listView) => {
registerChild(listView)
this.listView = listView
}}
/>
)
}}
</InfiniteLoader>
)
}
}
In the current situation there is no way to preserve view position after content has been changed in the rows. Because recomputeRowHeights
will call a re-render event that will trigger multiple onRowRendered
callbacks and there is no way to know when re-computations are done and scrollToRow
can be called, even then scrollToRow
in itself triggers multiple row re-renders.
Hello @ChoppinBlockParty, I am having the same issue, did you manage to fix it?
+1 here having a similar issue where recomputing the row height by changing height={rowContainerHeight}
causes the list to lose its position. can't come up with a solve because of the fact the onRowRendered
fires off like crazy, as chopping described. there's no simple way to hold on to the last viewed row index and scroll back to it bc it fires off so many times.