Efficient way to scroll to certain index in FlatList with variable item size
You can dynamically split your data according to scroll direction. If scroll goes up prepend data to your state and same for opposite direction. Then use onScrollToIndexFailed
like this :
<FlatList
ref={this.flatListRef}
data={this.state.data}
renderItem={() => <View> <SomeComponent /> </View>}
initialNumToRender={this.state.data.length / 5}
onEndReached={(e) => {
// Append data
}}
onScroll={(e) => {
if (e.nativeEvent.contentOffset.y == 0) {
// Prepend data
}
}}
onScrollToIndexFailed={(error) => {
this.flatListRef.scrollToOffset({ offset: error.averageItemLength * error.index, animated: true });
setTimeout(() => {
if (this.state.data.length !== 0 && this.flatListRef !== null) {
this.flatListRef.scrollToIndex({ index: error.index, animated: true });
}
}, 100);
}}
/>
You can workaround this issue. This worked for me and took me a lot of time to get that work :))
Related videos on Youtube
user1677104
Updated on September 15, 2022Comments
-
user1677104 over 1 year
I'm having a trouble adding scroll/jump to certain index functionality on FlatList in react-native. My FlatList items are vary in size (height) which makes me unable to implement
getItemLayout
since this requires me to have prior knowledge about the FlatList item size, therefore I cannot usescrollToIndex
(which requiresgetItemLayout
to be implemented).My solution was to get each item's size when rendered by using
onLayout
and map them with their index. I can then use each item size to get their offset and usescrollToOffset
to jump to the given item (by usingscrollToItem
function in the code below). The issue here is that I am not able to jump to certain item until that item has been rendered. My temporary solution for that is by tweakinginitialNumberToRender
close to the number of data and set thewindowSize
props as high as possible so that all of the items will be rendered (even though the user doesn't scroll down).getOffsetByIndex = (index) => { let offset = 0; for (let i = 0; i < index; i++) { const elementLayout = this.layoutMap[index]; if (elementLayout && elementLayout.height) { offset += this.layoutMap[i].height; } } return offset; }; scrollToItem = (index) => { const offset = this.getOffsetByIndex(index); this.flatListRef.scrollToOffset(offset); }; addToLayoutMap = (layout, index) => { this.layoutMap[index] = layout; }; render(){ return( <FlatList ref={this.flatListRef} data={this.state.data} renderItem={() => <View onLayout={this.addToLayoutMap}> <SomeComponent/> </View>} initialNumToRender={this.state.data.length / 5} windowSize={this.state.data.length} /> ); }
This solution works with small number of data, but when the data is large (containing ~300 of rows), it will take long time to be rendered get all the item size, preventing the user to jump to the last item directly for example.
Is there any efficient way to do it? Also, rendering all the rows is so memory consumptive and negates the benefit of using FlatList.
-
user1677104 over 4 yearsThanks for your detailed answer. Let me try to digest and try it later. A quick question tho, you are suggesting me to use
scrollToIndex
? SinceonScrollToIndexFailed
is called when failed to scroll to particular index. I think usingscrollToIndex
requiresgetItemLayout
to be implemented, which in my current case cant be done accurately due to varied item's size -
Matin Zadeh Dolatabad over 4 yearsNo
scrollToIndex
does not depend ongetItemLayout
. You can use scrollToIndex anytime you want! -
Matin Zadeh Dolatabad over 4 years@user1677104 Notice: Please remove
initialNumToRender
:( That may cause performance problems -
user1677104 over 4 yearswell noted, thanks for your answer, will post follow up result once I have given it a try. Currently having build problem
-
user1677104 over 4 yearshi, I've tried it, the problem is, when I try to scroll to index of the data that hasn't been fetched, I got scrollToIndex out of range error, since the initial data hasn't been appended. So the
onScrollToIndexFailed
isn't even executed here since FlatList first checks for the length of the data, if the desired index is within the range of the data, it would then try to scroll to it and executeonScrollToIndexFailed
when the index in fact hasn't been rendered. How do you work around this issue? -
user1677104 over 4 yearsI end up using
scrollToIndex
and useonScrollToIndexFailed
like what you provided. I didn't split my data as what you suggested due to issue I mentioned above, still gives some stutter, but tolerable. -
Matin Zadeh Dolatabad over 4 years@user1677104 Wait for async task to finish then initialize and scroll.
-
Yasir over 4 yearsHi there, I am trying something similar with
scrollToIndex
but itsviewOffset
got me stumped. the list should scroll up to and stop aboveKeyboard
however, for the first couple of items on the list, theviewOffset
value pushes them up, whereas scrolling down the list item and keyboard shows up, it behaves correctly. I am not sure how can I control theviewOffset
-
ThinkAndCode about 4 yearsMay I know how could you figure out the
setTimeout
time as 100ms -
Matin Zadeh Dolatabad about 4 years@Kishore You can set them after setState callback and remove
setTimeout
-
ThinkAndCode about 4 years@MatinZD Ok, But I am calling
ScroToIndex' after app state update as I have
FlatList` data in app state. what Can I do in this case? -
Matin Zadeh Dolatabad about 4 years@Kishore Please post your code somewhere so I can see that
-
asubanovsky about 4 yearsYou save my day, @MatinZD. Thanks a lot.
-
Andre Zimpel about 4 yearsMan this finally worked for me after failing for 2 hours!
-
Ponleu almost 4 yearsIt takes me more than a year to realize that onScrollToIndexFailed is this useful. Thanks man. It works...
-
Bhavya Khurjawal over 3 yearsYou are a lifesaver bro. I was really stuck at it. Thanks a lot!