React Native — Nested Virtualization Anti-pattern (Performance Optimization)
ERROR “ VirtualizedLists should never be nested inside plain ScrollViews”.
🔥 I have published an In Depth “React Native Advanced Guide Book” which secured ≈1000 GitHub STAR. (Free Book Link)
React Native provides some components for rendering large lists of data efficiently: <FlatList>
and <SectionList>
. These components are based on the VirtualizedList
component, which implements a virtualization technique to improve memory consumption and performance.
Virtualization means that only the items that are currently visible on the screen (or within a certain window size) are rendered, while the rest are replaced by blank spaces of the same size. This way, the list can handle thousands of items without affecting the app’s responsiveness or memory usage.
Nested VirtualizedLists Error
However, there is a common mistake that developers make when using these components: nesting them inside a plain ScrollView
with the same orientation (horizontal or vertical). Ultimately it creates a silent error in metro like bellow:
Error: ❝
VirtualizedLists
should never be nested inside plain ScrollViews ❞
Let’s see an example of Error View (FlatList
inside ScrollView
):
Error Reason
So, what are the problems when you nested a VirtualizedList
component inside a plain ScrollView
? Why was that error sent to your metro?
The are several reasons why nested VirtualizedList
is an Anti-pattern. Such as:
- The
VirtualizedList
cannot calculate the correct window size, because theScrollView
takes up the entire screen and does not constrain its content. Therefore, theVirtualizedList
will try to render all the items at once, defeating the purpose of virtualization and potentially causing performance issues or crashes. - The
ScrollView
will intercept all the touch events and prevent the VirtualizedList from handling them properly. This can affect features like pull-to-refresh, infinite scrolling, or swipe actions. - The
ScrollView
will also interfere with the scroll position and momentum of theVirtualizedList
, causing a janky and inconsistent user experience.
Quick solution before details explanation
To avoid these problems, React Native warns you when you nest a VirtualizedList
inside a plain ScrollView
with the same orientation, and suggests you to use another VirtualizedList-backed container instead. This means that you should either:
- Use a different orientation for the nested list (for example, a horizontal
FlatList
inside a verticalScrollView
). - Use another component that supports virtualization and scrolling, such as
<SectionList>
or<FlatGrid>
. - Use a custom component that implements its own logic for rendering and scrolling, such as
<RecyclerListView>
or<LargeList>
.
By following these suggestions, you can ensure that your lists are rendered efficiently and smoothly, without compromising the user experience or the app performance.
Let’s get started with details explanation & solution.
Error Code
So, in my case I had a code a where I needed to show a list of Images in FlatList
and at the top of the list there was a Title
& a camera button
to take more photos. The total view was wrapped by a ScrollView
. Let’s see this anti-pattern view 👇
So, from the above picture we see that FlatList
(red box) was wrapped by a ScrollView
(green box). This is Anti-Pattern.
Now let’s see this anti-pattern code 👇
Solution Code
We can solve it by using only the FlatList
instead of using both FlatList with ScrollView. Flatlist
made it really simple by providing the support for these two props functions. These 2 are:
- Header support (
ListHeaderComponent
) - Footer support (
ListFooterComponent
)
So we will use ListHeaderComponent
props function to solve our above problem for which the nested FlatList was used.
So, basically our problem was that we needed to make the header (Tittle
& camera button
) as scrollable. So, to do that we used ScrollView
. But now we can do that by using FlatList ListHeaderComponent
props function.
So, what we will do here is Just Wrap that header (Title
& Camera button
) into a <View>
component & then pass that component into FlatList ListHeaderComponent
props function.
Let’s see the code bellow. First, create a wrapper component for Title
& Camera button
like bellow.
ListHeaderComponent
props functionNow just add this gearHeader()
function as the ListHeaderComponent
props of FlatList
like bellow
ListHeaderComponent props
Here is the view of the solution
Here is the final code with ListHeaderComponent
props function
// Header function
function gearHeader() {
return (
<View style={styles.titleHolder}>
<Text style={styles.myGearText}>My Gear</Text>
<View style={styles.iconRow}>
<TouchableOpacity
onPress={() => openCameraCameraRoll(IMAGE_FOR.FIELD_GEAR)}>
<Icon name={'camera'} type={'entypo'} size={25} color="grey" />
</TouchableOpacity>
</View>
</View>
)
}
// FlatList "ListHeaderComponent" props
return (
<FlatList
data={gearPhotos}
keyExtractor={(item) => item._id}
numColumns={3}
ListHeaderComponent={gearHeader}
showsVerticalScrollIndicator={false}
renderItem={renderItem}
/>
)
Video on how ListHeaderComponent
props behave 👇
ListHeaderComponent
props behaveWhat next?
Now that you have successfully solved the virtualization anti-pattern by using the power of FlatList
, you might want to learn more about React Native virtualization and FlatList
performance optimization to load 1000+ items. Don’t worry, I have prepared another detailed article for you on this topic.
Article Link: React Native — Virtualization Performance Optimization (FlatList, SectionList, VirtualizedList, ScrollView)
Thank you for reading this article. I enjoy sharing my 5 years of experience in JavaScript, React, React-native & Node.js with you every day.
If you enjoyed reading this article, I would appreciate it if you could follow me on Twitter & Medium. You can also leave your feedback and comments there. Thank you for your support and interest.