react-native-web: FlatList performance: Scrolling is buggy and not smooth when virtualization is enabled

The problem

It have a vertical FlatList with not that many items (~150). When virtualization is enabled and I scroll, there are lot’s of FPS loss, I can see the lag clearly. Every time I scroll a little bit and a new dom element is added by the virtualization it happens.

On iOS simulator it’s fast and smooth. On chrome is the worse, firefox is less bad but still not good.

If I use disableVirtualization, then it becomes super smooth, but it has its consequences, e.g. switching between screens is much more expensive.

I played with all the virtualization options, didn’t help much.

Twitter is quite fast and also has dynamic height, does it use the same FlatList we use? If not, would love if they could open source it.

I’ve considered react-window but it doesn’t seem to support dynamic height (just-in-time measurement) yet. https://github.com/bvaughn/react-window/issues/6. Maybe react-virtualized could help. The problem is that these external solutions don’t support things like scrollToItem, onViewableItemsChanged, etc 😦

GIF comparison

With virtualization enabled + getItemLayout forcing height:100

Slow and buggy

Kapture 2019-05-04 at 22 19 59

Without virtualization (disableVirtualization)

Scrolling is fast and smooth (screen change / first render is slower; memory usage is higher)

Kapture 2019-05-04 at 22 22 22

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 12
  • Comments: 32 (14 by maintainers)

Commits related to this issue

Most upvoted comments

PureComponent, keyExtractor

Thanks but I use that already.

getItemLayout

Just tried, unfortunately didn’t help. Used getItemLayout to fix the height in 100. When scrolling, it still makes small jumps when the virtualization adds new items. The only thing that worked so far was disableVirtualization.

Only add disableVirtualization and worked for me.

I solved this in my app by remaking my renderItem component from scratch. It went from terrible, glitchy performance to butter. I was originally passing entire data objects to each list item. Then, in each component, I was checking my SWR cache to get more data about them.

For context, I was displaying an infinite scroll search list of artists (think Netflix search.) I wanted to show users if they’ve liked the artist before or not, etc. I was originally looping through a list, passing each list item down to my component, and then grabbing metadata about my list item from within each component. Why not? After all, each component could just access the global state, right? Turns out, this was very expensive.

I changed this by turning each card into a fully “dumb” component, with very little code. It only receives primitive props (hasLiked: boolean, starRating: number) and memoized functions (onPress(id: string)). This helped dramatically.

Oddly, adding getItemLayout caused it to flicker a lot on native. This is pretty surprising, given that I am using deterministic sizes. 🤷‍♂️

I highly recommend making your list item nimble. Don’t pass the entire list object down to it – only the data it needs. Keep any functions at the top level of the list component, and memoize them before passing them down to each item. And, as people have mentioned here, memoize each list item if they re-render too often and you’re seeing issues.

In case it’s useful, these are the props I ended up with. I memoized every function I passed to FlatList (this might be overkill, but it worked for me.)

<FlatList
  data={hits}
  removeClippedSubviews={Platform.OS !== 'web'}
  renderItem={renderItemFast} 
  ListEmptyComponent={ListEmptyComponent} // memoized function
  numColumns={itemsPerRow}
  // force re-render when items per row changes, since this can't be changed on the fly
  key={`list-${itemsPerRow}`} 
  initialNumToRender={10}
  keyExtractor={keyExtractor}
  onEndReached={fetchMore}
  keyboardDismissMode="on-drag"
  keyboardShouldPersistTaps="handled"
  style={style.container} 
/>

FlatList wasn’t built to support the web, where the solution would probably have to involve juggling a bunch of web APIs to try to find extra performance from somewhere. I don’t think supporting the web is a priority at the moment (cc @sahrens), but FB might eventually have a cross-platform solution and Google is working on a built-in virtualizer that might be something we can use one day.

I noticed on iPhone 6s and Samsung Galaxy S8 (using Android 8) scrolling with flatlist is very smooth, but I tried two phones using Android 9 (Samsung Galaxy S10, Essential Phone) and scrolling was very choppy. I switched over to RecycleListView and now all phones scroll very smooth without jank.