Efficient way to scroll to certain index in FlatList with variable item size

11,439

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 :))

Share:
11,439

Related videos on Youtube

user1677104
Author by

user1677104

Updated on September 15, 2022

Comments

  • user1677104
    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 use scrollToIndex (which requires getItemLayout 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 use scrollToOffset to jump to the given item (by using scrollToItem 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 tweaking initialNumberToRender close to the number of data and set the windowSize 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
    user1677104 over 4 years
    Thanks for your detailed answer. Let me try to digest and try it later. A quick question tho, you are suggesting me to use scrollToIndex? Since onScrollToIndexFailed is called when failed to scroll to particular index. I think using scrollToIndex requires getItemLayout to be implemented, which in my current case cant be done accurately due to varied item's size
  • Matin Zadeh Dolatabad
    Matin Zadeh Dolatabad over 4 years
    No scrollToIndex does not depend on getItemLayout. You can use scrollToIndex anytime you want!
  • Matin Zadeh Dolatabad
    Matin Zadeh Dolatabad over 4 years
    @user1677104 Notice: Please remove initialNumToRender :( That may cause performance problems
  • user1677104
    user1677104 over 4 years
    well noted, thanks for your answer, will post follow up result once I have given it a try. Currently having build problem
  • user1677104
    user1677104 over 4 years
    hi, 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 execute onScrollToIndexFailed when the index in fact hasn't been rendered. How do you work around this issue?
  • user1677104
    user1677104 over 4 years
    I end up using scrollToIndex and use onScrollToIndexFailed 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
    Matin Zadeh Dolatabad over 4 years
    @user1677104 Wait for async task to finish then initialize and scroll.
  • Yasir
    Yasir over 4 years
    Hi there, I am trying something similar with scrollToIndex but its viewOffset got me stumped. the list should scroll up to and stop above Keyboard however, for the first couple of items on the list, the viewOffset 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 the viewOffset
  • ThinkAndCode
    ThinkAndCode about 4 years
    May I know how could you figure out the setTimeout time as 100ms
  • Matin Zadeh Dolatabad
    Matin Zadeh Dolatabad about 4 years
    @Kishore You can set them after setState callback and remove setTimeout
  • ThinkAndCode
    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
    Matin Zadeh Dolatabad about 4 years
    @Kishore Please post your code somewhere so I can see that
  • asubanovsky
    asubanovsky about 4 years
    You save my day, @MatinZD. Thanks a lot.
  • Andre Zimpel
    Andre Zimpel about 4 years
    Man this finally worked for me after failing for 2 hours!
  • Ponleu
    Ponleu almost 4 years
    It takes me more than a year to realize that onScrollToIndexFailed is this useful. Thanks man. It works...
  • Bhavya Khurjawal
    Bhavya Khurjawal over 3 years
    You are a lifesaver bro. I was really stuck at it. Thanks a lot!