React-Native FlatList performance problems with large list
Here are some improvements you can do to optimize your flatlist:
- Pagination
- You can paginate your data in your backend and refetch it when your scroll gets closer to the end.
onEndReached
andonEndReachedThreshold
can help you.
- You can paginate your data in your backend and refetch it when your scroll gets closer to the end.
- Update your React Native version to 49 or latest
- Fiber 16 has amazing performance improvement which makes everything runs faster and smoother
- Use
PureComponent
for render itemPureComponent
improves the rendering and memory usage of your component, creating render items as pure gives you a better performance experience
- Define
getItemLayout
prop- It improves item rendering since React will previously know its layout definition
Hope it helps
Edit Dec 20 '19: The information on this answer became part of the official docs. You should check it out!
Edit May 26 '18: This answer became a bigger and more complete post on github
If you follow this thread, you'll see that react's team is already aware of this performance issue.
There is no silver bullet for this issue, you have to consider the trade offs of every approach and what you think is a good experience for your audience. But fortunately there are several tweaks you can try and improve your FlatList
.
Terms and meanings
There are a lot of terms used (on docs or some issues) that were confusing for me at first. So, let's get this out of the way from the start.
VirtualizedList is the component behind
FlatList
, and is React Native's implementation of the 'virtual list' concept.Performance, in this context, imply a smooth (not choppy) scroll (and navigation in or out of your list) experience.
Memory consumption, in this context, is how much information about your list is being stored in memory, which could lead to a app crash.
Blank areas means that the VirtualizedList couldn't render your items fast enough, so you enter on a part of your list with non rendered components.
Window here is not your viewport but rather, size of the area in which items should be rendered.
Props
One way to improve your FlatList
is by tweaking it's props. Here are a list of props that can help your with that.
removeClippedSubviews
You can set the removeClippedSubviews
prop to true, which unmount components that are off of the window.
Win: This is very memory friendly, as you will always have a little rendered list.
Trade offs: Be aware, that this implementation can have bugs, such as missing content if you use it on a component that will not unmount (such as a navigation route). It also can be less performant, having choppy scroll animations for big lists with complex items on not-so-good devices, as it make crazy amounts of calculations per scroll.
maxToRenderPerBatch
You can set the maxToRenderPerBatch={number}
, which is a VirtualizedList
prop that can be passed directly to FlatList
. With this, you can control the amount of items rendered per batch, which is the next chunk of items rendered on every scroll.
Win: Setting a bigger number means less visual blank areas when scrolling (a better the fill rate).
Trade offs: More items per batch means less JavaScript performance, which means less responsiveness (clicking a item and opening the detail). If you have a static and non-interactive list, this could be the way to go.
initialNumToRender
You can set the initialNumToRender={number}
. This means the initial amount of items to render.
Win: You can set this value to the precise number of items that would cover the screen for every device. This can be a big performance boost when rendering the list component.
Trade offs: You are most likely to see blank areas when setting a low initialNumToRender
.
windowSize
You can set the windowSize={number}
. The number passed here is a measurement unit where 1 is equivalent to your viewport height. The default value is 21, being 10 viewports above, 10 below, and one in between.
Win: If you're worried mainly about performance, you can set a bigger windowSize
so your list will run smoothly and with less blank space. If you're mainly worried about memory consumption, you can set a lower windowSize
so your rendered list will be smaller.
Trade offs: For a bigger windowSize
, you will have a bigger memory consumption. For a lower windowSize
, you will have lower performance and bigger change of seeing blank areas.
legacyImplementation
This prop, when true, make your FlatList
rely on the older ListView
, instead of VirtualizedList
.
Win: This will make your list definitely perform better, as it removes virtualization and render all your items at once.
Trade offs: Your memory consumption goes to the roof and chances are good that a big list (100+) with complex items will crash your app. It also fires a warning that the above tweaks will not work, because you're now on a ListView.
disableVirtualization
You will see people advocation the use of this prop on some issues. But this is deprecated now. This used to do something similar to legacyImplementation
.
List items
There are also some win-win strategies that involves your list item components. They are being managed by VirtualizedList a lot, so they need to be fast.
Use simple components
The more complex your components are, the slower they will render. Try to avoid a lot of logic and nesting in your list items. If you are reusing this list item component a lot in your app, create a duplicate just for your big lists and make them with less logic as possible and less nested as possible.
Use light components
The heavier your components are, the slower they render. Avoid heavy images (use a cropped version for list items, as small as possible). Talk to your design team, use as little effects and interactions and information as possible in your list. Save them to your item's detail.
Use shouldComponentUpdate
Implement update verification to your components. React's PureComponent is mostly for when you don't have time to think. If you're reading this, you do have time, so, create the strictest rules for your list item components. If your list is simple enough, you could even use
shouldComponentUpdate() {
return false
}