React Native - VirtualizedList: You have a large list that is slow to update

Viewed 5338

Introduction

I am having serious performance issues with my VirtualizedList (infinite scroll)... I have been following this to improve it but still working really slow.

When I scroll down, the UI threads go from 60fps to 30fps, and the JS thread from 60fps to 10/20fps (sometimes to 0fps). On iOS (iPhone 6) it seems to go smoothy than on my Android (Samsung Galaxy S8+)

My VirtualizedList is rendering items with different heights, and it fetches the data when its end is reached. My item extends from React.PureComponent, as it is recommended:

VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. Object { "contentLength": 20720, "dt": 532, "prevDt": 2933, }

This issue comes when I scroll down around 20 items...

Code

Here is the code of the VirtualizedList:

const keyExtractor = ({ id }) => id;
...
 
getItem = (data, index) => data[index];

getItemCount = (data) => data.length;

renderItem = ({ item, index }) => {
 const {
   images,
   dimensions,
   description,
   location,
   likes,
   comments,
   date,
 } = item;

 return (
   <Card
     images={images}
     postDimensions={dimensions}
     description={description}
     location={location}
     likes={likes}
     comments={comments}
     date={date}
   />
 );
};

<VirtualizedList
    data={data}
    getItem={this.getItem}
    getItemCount={this.getItemCount}
    keyExtractor={keyExtractor}
    listKey={listKey}
    legacyImplementation={false}
    numColumns={1}
    renderItem={this.renderItem}
    initialNumToRender={MAX_POSTS_TO_RETRIEVE_LENGTH} // 10
    windowSize={32}
    maxToRenderPerBatch={15}
    updateCellsBatchingPeriod={50}
    removeClippedSubviews={false}
    ListFooterComponent={this.renderFooter}
    ListEmptyComponent={ListEmptyComponent}
    onEndReached={onEndReached}
    onEndReachedThreshold={0.5}
/>

Pd: each item I am rendering has an unique id.

What I have tried

I have tried to implement my own getItemLayout method for the List but for some reason it doesn't work properly.

Warning: Failed frame type: The frame frame.length is marked as required in VirtualizedList.getItemLayout, but its value is undefined.

itemHeights = [];

getItemLayout = (data, index) => {
  const length = this.itemHeights[index];
  const offset = this.itemHeights.slice(0,index).reduce((a, c) => a + c, 0)
  return {length, offset, index}
}

renderItem = ({ item, index }) => {
 const {
   images,
   dimensions,
   description,
   location,
   likes,
   comments,
   date,
 } = item;

 return (
   <View onLayout={(event) => this.itemHeights[index] = event.nativeEvent.layout.height}>
    <Card
      images={images}
      postDimensions={dimensions}
      description={description}
      location={location}
      likes={likes}
      comments={comments}
      date={date}
    />
   </View>
 );
};

<VirtualizedList
    data={data}
    getItem={this.getItem}
    getItemCount={this.getItemCount}
    getItemLayout={this.getItemLayout}
    keyExtractor={keyExtractor}
    listKey={listKey}
    legacyImplementation={false}
    numColumns={1}
    renderItem={this.renderItem}
    initialNumToRender={MAX_POSTS_TO_RETRIEVE_LENGTH} // 10
    windowSize={32}
    maxToRenderPerBatch={15}
    updateCellsBatchingPeriod={50}
    removeClippedSubviews={false}
    ListFooterComponent={this.renderFooter}
    ListEmptyComponent={ListEmptyComponent}
    onEndReached={onEndReached}
    onEndReachedThreshold={0.5}
/>

Any ideas? I have seen that it is a common problem but I haven't found any solution.

UPDATE

To solve the issue I was getting with getItemLayout just do:

getItemLayout = (data, index) => {
  const length = this.itemHeights[index] || 0; // <----- if undefined return 0
  const offset = this.itemHeights.slice(0,index).reduce((a, c) => a + c, 0)
  return {length, offset, index}
}
1 Answers

If this happen to someone, just use my custom getItemLayout function and follow these steps

Using those values has worked for me.

    initialNumToRender={10}
    windowSize={5}
    maxToRenderPerBatch={5}
    updateCellsBatchingPeriod={30}
    removeClippedSubviews={false}
    ...
    onEndReachedThreshold={0.1}

Pd: my viewport is covered with 1.5 items

Related