I've been struggling with this issue for the past couple of days: I have a list of creators stored in an array, along with another array for followed creators, but when the user 'follows' a creator the whole FlatList re-renders. This is an issue because I'm loading 3 images for each creator, so there's a lot of flickering and lag every time the user follows/unfollows a creator.
I've tried using React.memo but it doesn't seem to be working and I feel like there may be issues in other parts of my code as well. Any help would be greatly appreciated!
Getting data on first render:
useMemo(() => {
setRefreshing(true);
return onValue(ref(db, '/users/' auth?.currentUser?.uid '/vendorsFollowing'), async (querySnapShot) => {
let data = (await querySnapShot.val()) || {};
let vendorData = { ...data };
setVendorsFollowing(Object.keys(vendorData))
setRefreshing(false);
});
}, []);
useMemo(() => {
setRefreshing(true);
return onValue(ref(db, '/users'), (querySnapShot) => {
let data = querySnapShot.val() || {};
let vendorList = { ...data };
setVendorArray(vendorList);
setFilteredVendorArray(vendorList);
setRefreshing(false);
});
}, []);
VendorItem:
const VendorItem = React.memo(({ vendor }: any) => {
// image stuff
const [imgUrl1, setImgUrl1] = useState<string | undefined>(undefined);
const ref1 = ref_storage(storage, vendor.uid '_1.png');
const [imgUrl2, setImgUrl2] = useState<string | undefined>(undefined);
const ref2 = ref_storage(storage, vendor.uid '_2.png');
const [imgUrl3, setImgUrl3] = useState<string | undefined>(undefined);
const ref3 = ref_storage(storage, vendor.uid '_3.png');
if (!blocked) {
getDownloadURL(ref1)
.then((url) => {
setImgUrl1(url);
})
.catch((error) => {
});
getDownloadURL(ref2)
.then((url) => {
setImgUrl2(url);
})
.catch((error) => {
});
getDownloadURL(ref3)
.then((url) => {
setImgUrl3(url);
})
.catch((error) => {
});
}
return(
<View style={[styles.eventImageContainer, { marginVertical: (imgUrl1 || imgUrl2 ||
imgUrl3) && 5 }]}>
{imgUrl1 && <Image source={{ uri: imgUrl1 }} style={styles.vendorImage}
imageStyle={{ borderRadius: 20 }} />}
{imgUrl2 && <Image source={{ uri: imgUrl2 }} style={styles.vendorImage}
imageStyle={{ borderRadius: 20 }} />}
{imgUrl3 && <Image source={{ uri: imgUrl3 }} style={styles.vendorImage}
imageStyle={{ borderRadius: 20 }} />}
</View>
// updateStarred basically adds vendor.uid to vendorsFollowing and to the database
<TouchableOpacity onPress={() => { updateStarred(vendor.uid) }}>
<Icon
name={vendorsFollowing.includes(vendor.uid) ? 'bookmark' : 'bookmark-o'}
size={25}
color="white"
/>
</TouchableOpacity>
)
}, (prevProps, nextProps) => {
if (vendorsFollowing.includes(prevProps.vendor.uid) === vendorsFollowing.includes(nextProps.vendor.uid)) return true;
return false;
});
Edit: FlatList:
<FlatList
initialNumToRender={7}
contentContainerStyle={{ paddingBottom: 325 }}
showsVerticalScrollIndicator={false}
style={{ marginTop: 10 }}
data={Object.keys(filteredVendorArray)}
keyExtractor={(item) => filteredVendorArray[item].uid}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={loadNewData} />}
renderItem={({ item }) => (
<View style={{ backgroundColor: '#FFfF8F3', marginBottom: 20 }}>
<VendorItem vendor={filteredVendorArray[item]} />
</View>
)}/>
CodePudding user response:
As a renderItem
property, you provide an anonymous function which is always created and trigger a new render. Replace it:
renderItem={({ item }) => (
<View style={{ backgroundColor: '#FFfF8F3', marginBottom: 20 }}>
<VendorItem vendor={filteredVendorArray[item]} />
</View>
)}
with:
const renderItemFn = useCallback(() => ({ item }) => (
<VendorItem vendor={filteredVendorArray[item]} />
))
...
renderItem={renderItemFn}
The same problem in keyExtractor.
Also move View View style={{ backgroundColor: '#FFfF8F3', marginBottom: 20 }}
to VendorItem because it doesn't have memorization
More examples of flatlist optimizations here: https://www.obytes.com/blog/a-guide-to-optimizing-flatlists-in-react-native
Yet another possible problem is your memoization, if vendor
is a complex object you need to add a custom compare function:
const customComparator = (prevProps, nextProps) => {
// add your logic for comprassion
return nextProps. vendor === prevProps. vendor;
};
export default React.memo(VendorItem, customComparator);