I am using a refreshControl component in SectionList, it works as expected pre-build and on iOS, however on Android phones sometimes the refresh icon appears and flickers continuously without initiating a refresh. Here is the reproduction.
The code is as follows:
import React, { useEffect, useState, useRef, useCallback, useContext } from 'react'
import { View, Text, AppState, RefreshControl, Pressable, SectionList } from 'react-native'
...
const AnimatedSectionList = Animated.createAnimatedComponent(SectionList)
export const DiscoverOffersScreen = ({ route }: Props) => {
// Hooks
const qc = useQueryClient()
...
// States
const [refreshing, setRefreshing] = useState(false)
...
// Offer sections
const [favoriteOffers, setFavoriteOffers] = useState<OfferCard[]>([])
const [expiringOffers, setExpiringOffers] = useState<OfferCard[]>([])
const [filteredOffers, setFilteredOffers] = useState<OfferCard[]>([])
// Queries
const { data, isLoading, refetch } = useQuery<OffersQuery>({
queryKey: ['offers'],
queryFn: getOfferCardsWithFavorites
})
// Animations & styling
...
const load = async () => {
try {
registerForPushNotificationsAsync()
const locations = await getAllBusinessLocations()
setLocations(locations)
}
catch (error) {
if (error instanceof EmptyAccessTokenError) {
logOut(navigation)
}
}
}
// Refresh logic
const onRefresh = async () => {
console.log("refreshed")
setRefreshing(true)
await load()
await refetch()
refreshTimeout()
}
const refreshTimeout = useCallback(() => {
setTimeout(() => {
setRefreshing(false)
}, 400)
}, [])
// Refresh queries when app is resumed from background
useEffect(() => {
load()
const subscription = AppState.addEventListener('change', nextAppState => {
if (
appState.current.match(/inactive|background/) &&
nextAppState === 'active'
) {
onRefresh()
}
appState.current = nextAppState
})
return () => {
subscription.remove()
}
}, [])
// Define sections that are displayed
const sections : Section[] = [
{
index: 0,
title: 'My Favorites',
data: favoriteOffers
},
{
index: 1,
title: 'Expiring Soon',
data: expiringOffers
},
{
index: 2,
title: expiringOffers.length + favoriteOffers.length === 0 ? 'All Offers' : 'Other Offers',
data: filteredOffers
}
]
return (
<>
// Header
...
<AnimatedSectionList
entering={SlideInLeft}
exiting={SlideOutLeft.duration(200)}
refreshControl={<RefreshControl tintColor={'#519478'} colors={['#519478']} refreshing={refreshing} onRefresh={onRefresh} />}
contentContainerStyle={{ paddingHorizontal: '5%' }}
stickySectionHeadersEnabled={false}
sections={sections}
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
initialScrollIndex={0}
ref={sectionListRef}
keyExtractor={(item) => (item as OfferCard).offer_id.toString()}
renderSectionHeader={(info) => (
...
)}
renderItem={({ item, index, section }) => {
return <FloatingOfferCard card={item as OfferCard}/>
}}
/>
...
</View>
</>
)
}
I'm also using react-query and react-native-reanimated as you can see in the code.