React Native refresh control icon flickering

26 Views Asked by At

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.

0

There are 0 best solutions below