I am going to post a description of the problem we face and steps we tried to take it to address it, as well as screenshots from profiler.
For some context, we are small startup where we have a list of snaps/moments displayed in chronological order in 2 columns. Naturally, a user can view hundreds of such images as they scroll. The issue is that our memory spikes a lot, and we can't figure out how to control it.
We use the library here as we found it to be better than its alternatives for following reasons:
We face the spikes with all libraries, except for Image but that's because this library doesn't load the images in the first places.
For rendering, we use Flatlist and have utilized all props, but for now the following props are being used to control memory:
We used the onEndReached prop to trigger a fetch for next posts. This fetch consists of making firebase calls and updating the list that we pass in the data field for flatlist. Inside each item, images are fetched with CDNs and we have verified that all images are < 5MB and ~95% are <= 1MB.
Flashlist doesn't help with memory and also the images flicker a lot when scrolled fast, and that is unacceptable.
We have tried clearing the cache of FasterImage with clearCache, and have tried calling the GC manually but neither approach works.
The app initially starts off with ~500-600MB on Android, and rises up to 2GB when scrolled fast enough. Capturing the heap dump reveals that majority of the Native Memory is occupied by Bitmap (android.graphics). I have attached a screenshot of the same.
Is there a way to reduce the memory footprint by manually calling some function? Has anyone faced this issue in React Native? Any help would be appreciated. Also, we face this in iOS too but I am the Android dev and don't have the logs for iOS. For iPhones, the maximum spike is ~850MB.
Our flatlist code looks like this:
import { FasterImageView } from '@candlefinance/faster-image'
<FlatList
onEndReached={() => fetch(true)}
onEndReachedThreshold={0.5}
ref={scrollRef}
style={{ flex: 1, overflow: 'hidden' }}
initialNumToRender={4}
windowSize={4}
maxToRenderPerBatch={4}
removeClippedSubviews={true}
data={Object.keys(postsKey)}
keyExtractor={item => item}
renderItem={({ item, index }) => (
<View key={index} style={{ overflow: 'hidden', marginTop: 20 }}>
<Text style={{ ...getFontFamily('Montserrat', 600), fontSize: 17, lineHeight: 18, color: '#000', marginBottom: 10, marginLeft: 15 }}>{item}</Text>
<FlatList
scrollEnabled={false}
initialNumToRender={4}
windowSize={4}
maxToRenderPerBatch={4}
removeClippedSubviews={true}
data={postsKey[item]}
renderItem={({ item, i }) => <PostCard key={i} post={item} index={i} posts={allPosts} setDisplayIndex={setDisplayIndex} userStreaks={userStreaks} userProfilePictures={userProfilePictures} />}
keyExtractor={item => item.id}
numColumns={2}
showsHorizontalScrollIndicator={false}
/>
</View>
)}
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={() => fetch(false)} tintColor={DEFAULT_COLOR} />
}
/>
I had this problem with rendering multiple charts in a flatlist.
Shopify solved the problem with this alternative that replaces react-svg with skia. Skia is a much more efficient rendering engine.
Since you have a flatlist inside a flatlist I'd suggest giving it ago.
https://shopify.github.io/flash-list/
it's also possible you're just using a development build and heavy renders don't perform well outside of the optimizations applied in a production build.