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.lengthis marked as required inVirtualizedList.getItemLayout, but its value isundefined.
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}
}