React Native provides some components for rendering large lists of data efficiently: and . 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.
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:
Let’s see an example of Error View (FlatList inside ScrollView):
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 the ScrollView takes up the entire screen and does not constrain its content. Therefore, the VirtualizedList 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 the VirtualizedList, causing a janky and inconsistent user experience.
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 vertical ScrollView).
-
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 or .
By following these suggestions, you can ensure that your lists are rendered efficiently and smoothly, without compromising the user experience or the app performance.
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 👇
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 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.
Now just add this gearHeader() function as the ListHeaderComponent props of FlatList like bellow
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}
/>
);
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 React-native, JavaScript, React & Node.js with you every day. If you enjoyed reading this article, I would appreciate it if you could follow me on Twitter & Medium.
If you find any ISSUE in this Guide BOOK, please create a PR to help the community 🔥