RecyclerListView: High performance ListView for React Native and Web

Talha Naqvi
4 min readAug 17, 2017

--

It has been around 10 months since we started working with React Native @Flipkart. It has been a great experience, we’ve built some amazing stuff using the tech but we’ve always hit road blocks in the following areas:

  1. User Interactions (Luckily we don’t do too many right now)
  2. Animation frame rate (Fixed with Native Driver)
  3. ListView/FlatList Performance

We’ve lot of areas in the app where we have to display huge list such as search results, filter options and reviews etc. We also need to toggle between listview/gridview in many cases and detect impression events. We really really needed a good listview.

TLDR: RecyclerView for React Native, which is a high performance list view written purely in JavaScript with staggered grid support. No frame drops or, blanks. Tested with React Native 0.30.0+, you may not need to upgrade just yet!

npm: https://www.npmjs.com/package/recyclerlistview

github: https://github.com/Flipkart/recyclerlistview

So, the problem?

ListView:

Let’s talk about ListView first, while it is great at maintaining overall frame rate where it struggles is the memory part and quick scrolls.

There is no virtualization of any kind leading to huge increase in memory usage as you scroll down a practically infinite list. The second problem is that listview listens to scroll event and renders items as you scroll down, this is great but it also means if you scroll too quickly the scroll would come to halt before items are rendered. This gives user the feeling of multiple end points in the same list. Brands list in our filter experience suffered from the same problem.

FlatList:

FlatList is amazing and comes with tons of features. It even lets you render separators and comes with a built in customisable view tracker which is something we were interested in.

FlatList is a virtualized listview and it does so by unmounting views that have gone out of viewport. It does help bring down overall memory usage down but there are cons to this approach:

  1. Views on native end get destroyed causing lot of garbage collection.
  2. Views need to be recreated now as you scroll up which might be a problem if the scroll is quick.
  3. Frame drops in case of complex templates both on scrolling up and down since views are being constantly created and destroyed.
  4. Things get worse if list items are small, lot of blanks visible on quick scrolls. On Android, scroll speed is faster than iOS making this a bigger challenge.

FlatList relies on scrollview to do its layout and appropriately replaces unmounted views with blank views. This is great since this enables sticky headers etc. FlatList is a huge upgrade but it still didn’t get us where we wanted to be.

The Solution?

Use recycling with a fast layout algorithm.

So, what did we do?

First, we used native RecyclerView (Android) and UICollectionView (iOS)

I will not dive deep into the solution but we wrote a ListView that used events from RecyclerView and UICollectionView to achieve recycling in react. We were very much inspired by the following post: https://medium.com/@talkol/recycling-rows-for-high-performance-react-native-list-views-628fd0363861

This did work great but the only problem was blanks on quick scrolls and making changes and adding new features was hard since it required changes in three places to continue parity across platforms. The difference between RecyclerView and UICollectionView posed some unique challenges as well. The primary problem here was that bridge use was pretty high with all those events being sent. Also, we have cross platform goals and these listviews had no way to really work on the web. So, to get around all these issues we decided to build a performant listview ourselves.

Second, we built RecyclerListView for Android/iOS/Web (JS Only)

RecyclerListView is a JS only listview written from scratch. If has most of the features that ListView had and can support staggered grid layouts. It makes it trivial to hit 60fps while maintaining memory efficiency using “cell recycling”. Instead of un-mounting we reclaim views outside of viewport to render new items. This prevents view creation which is expensive and keeps memory usage finite.

How does it work?

It works on three fundamental units:

  1. Layout/Data Providers
  2. Viewability Tracker
  3. Virtual Renderer

Using layout/data providers you specify your data, its type and estimated or, exact dimensions. Using these types RecyclerListView decides whether to create a new view or reuse an existing one which is not in viewport.

Viewability Tracker and Virtual Renderer work together, at any given offset viewability tracker informs virtual renderer about the state of the viewport (visible items etc) based on which it generates the render stack using what’s available wherever possible.

Given we support staggered grid and progressive rendering (only create what is necessary) means you can do all sorts of ListView and GridViews with variable heights and width. You can even switch between a ListView and GridView by just swapping your layout provider without losing scroll context!

RecyclerListView Scroll Performance

We’ll dig deep into the performance part of RecyclerListView in a future post. For now give it a try and let us know what you think of it!

Support

You don’t have to be on the latest version! Tested on react native 0.30.0+. Also works with React Native Web. We also include scroll components and renderers for ReactJS. Check github if you want to make it work on web without using react native web.

Get Started

Checkout the following links to get started and explore more features:

npm: https://www.npmjs.com/package/recyclerlistview

github: https://github.com/Flipkart/recyclerlistview

Contact Us!

Please open issues for any bugs that you encounter. You can reach out to me on twitter @naqvitalha or, write to cross-platform@flipkart.com for any questions that you might have.

--

--