Search code examples
react-nativereact-native-flatlistreact-querytanstackreact-query

FlatList sometimes does not rerender on data change


I am using react-query for data fetching in React Native. I have a Notifications screen. In this screen there are two queries

  • useGrocerNotifications -> returns last 10 notifications
  • useGrocerAllNotifications -> returns all notifications that includes last 10 notifications too

Note: these queries have different urls and query keys

useGrocerAllNotifications query is disabled by default. There is a button in page that called Load All. When user click this button I enable the useGrocerAllNotifications query and update FlatList data but it sometimes does not rerender.

Here is my code

Notifications.tsx

export function GrocerNotifications() {
  const [isPressedLoadAll, setIsPressedLoadAll] = useState(false)

  const queryClient = useQueryClient()

  const { data, refetch, isFetching } = useGrocerNotifications()
  const {
    data: allData,
    refetch: allRefetch,
    isFetching: isAllFetching
  } = useGrocerAllNotifications({
    enabled: isPressedLoadAll
  })

  const renderData = useMemo(() => {
    if (Array.isArray(allData) && allData.length > 0 && isPressedLoadAll) return allData

    if (Array.isArray(data) && data.length > 0) return data

    return []
  }, [allData, data, isPressedLoadAll])

  const contentContainerStyle = useBottomSafeStyle(styles.contentContainer)

  const handleLoadAllPress = useCallback(() => {
    queryClient.invalidateQueries({ queryKey: ['grocer', 'notifications', 'all'] })

    setIsPressedLoadAll(true)
  }, [queryClient])

  const renderItem: ListRenderItem<GrocerNotificationType> = useCallback(
    ({ item }) => (
      <GrocerNotification
        title={item.title}
        description={item.description}
        date={item.createdDate}
        isNew={!item.isRecieved}
      />
    ),
    []
  )

  const keyExtractor = useCallback(
    (item: GrocerNotificationType) => `grocer_notification_${item.id}`,
    []
  )

  const handleRefresh = useCallback(() => {
    if (isPressedLoadAll) {
      allRefetch()

      return
    }

    refetch()
  }, [refetch, allRefetch, isPressedLoadAll])

  return (
    <FlatList
      data={renderData}
      renderItem={renderItem}
      style={styles.flatList}
      contentContainerStyle={contentContainerStyle}
      keyExtractor={keyExtractor}
      refreshing={isFetching || isAllFetching}
      onRefresh={handleRefresh}
    />
  )
}

Data is updating correctly. I can see new data in console but it is not reflecting to FlatList

There is something wrong but I do not know what it is.

Here are the console logs

old data:
[
  {
    "id": 48,
    "title": "7 kasım aykut 9"
  },
  {
    "id": 47,
    "title": "7 kasım aykut 8"
  },
  {
    "id": 46,
    "title": "7 kasım aykut 7"
  },
  {
    "id": 45,
    "title": "7 kasım aykut 6"
  },
  {
    "id": 44,
    "title": "7 kasım aykut 5"
  },
  {
    "id": 43,
    "title": "7 kasım aykut 4"
  },
  {
    "id": 42,
    "title": "7 kasım aykut 3"
  },
  {
    "id": 41,
    "title": "7 kasım aykut 2"
  },
  {
    "id": 40,
    "title": "7 kasım aykut 1"
  },
  {
    "id": 39,
    "title": "6 kasım aykut 3"
  }
]
new data:
[
  {
    "id": 48,
    "title": "7 kasım aykut 9"
  },
  {
    "id": 47,
    "title": "7 kasım aykut 8"
  },
  {
    "id": 46,
    "title": "7 kasım aykut 7"
  },
  {
    "id": 45,
    "title": "7 kasım aykut 6"
  },
  {
    "id": 44,
    "title": "7 kasım aykut 5"
  },
  {
    "id": 43,
    "title": "7 kasım aykut 4"
  },
  {
    "id": 42,
    "title": "7 kasım aykut 3"
  },
  {
    "id": 41,
    "title": "7 kasım aykut 2"
  },
  {
    "id": 40,
    "title": "7 kasım aykut 1"
  },
  {
    "id": 39,
    "title": "6 kasım aykut 3"
  },
  {
    "id": 38,
    "title": "6 kasım aykut 2"
  },
  {
    "id": 35,
    "title": "6 kasım aykut 1"
  }
]

as you can see there are 2 new items with ids 38 and 35

but FlatList keyExtractor behaves strangely

Here are keyExtractor logs

old keyExtractor data:
runs twice on mount I do not know why
{
  "id": 48,
  "title": "7 kasım aykut 9"
}
{
  "id": 47,
  "title": "7 kasım aykut 8"
}
{
  "id": 46,
  "title": "7 kasım aykut 7"
}
{
  "id": 45,
  "title": "7 kasım aykut 6"
}
{
  "id": 44,
  "title": "7 kasım aykut 5"
}
{
  "id": 43,
  "title": "7 kasım aykut 4"
}
{
  "id": 42,
  "title": "7 kasım aykut 3"
}
{
  "id": 41,
  "title": "7 kasım aykut 2"
}
{
  "id": 40,
  "title": "7 kasım aykut 1"
}
{
  "id": 39,
  "title": "6 kasım aykut 3"
}
{
  "id": 48,
  "title": "7 kasım aykut 9"
}
{
  "id": 47,
  "title": "7 kasım aykut 8"
}
{
  "id": 46,
  "title": "7 kasım aykut 7"
}
{
  "id": 45,
  "title": "7 kasım aykut 6"
}
{
  "id": 44,
  "title": "7 kasım aykut 5"
}
{
  "id": 43,
  "title": "7 kasım aykut 4"
}
{
  "id": 42,
  "title": "7 kasım aykut 3"
}
{
  "id": 41,
  "title": "7 kasım aykut 2"
}
{
  "id": 40,
  "title": "7 kasım aykut 1"
}
{
  "id": 39,
  "title": "6 kasım aykut 3"
}
new keyExtractor data:
runs twice too
{
  "id": 48,
  "title": "7 kasım aykut 9"
}
{
  "id": 47,
  "title": "7 kasım aykut 8"
}
{
  "id": 46,
  "title": "7 kasım aykut 7"
}
{
  "id": 45,
  "title": "7 kasım aykut 6"
}
{
  "id": 44,
  "title": "7 kasım aykut 5"
}
{
  "id": 43,
  "title": "7 kasım aykut 4"
}
{
  "id": 42,
  "title": "7 kasım aykut 3"
}
{
  "id": 41,
  "title": "7 kasım aykut 2"
}
{
  "id": 40,
  "title": "7 kasım aykut 1"
}
{
  "id": 39,
  "title": "6 kasım aykut 3"
}
{
  "id": 38,
  "title": "6 kasım aykut 2"
}
{
  "id": 39, -> what happens here. Why does id 39 come again. This should be id 35 according to data
  "title": "6 kasım aykut 3"
}
 after this the data seems to revert back to old one ??
{
  "id": 48,
  "title": "7 kasım aykut 9"
}
{
  "id": 47,
  "title": "7 kasım aykut 8"
}
{
  "id": 46,
  "title": "7 kasım aykut 7"
}
{
  "id": 45,
  "title": "7 kasım aykut 6"
}
{
  "id": 44,
  "title": "7 kasım aykut 5"
}
{
  "id": 43,
  "title": "7 kasım aykut 4"
}
{
  "id": 42,
  "title": "7 kasım aykut 3"
}
{
  "id": 41,
  "title": "7 kasım aykut 2"
}
{
  "id": 40,
  "title": "7 kasım aykut 1"
}
{
  "id": 39,
  "title": "6 kasım aykut 3"
}

I have tried these solutions

  • adding extraData prop with renderData renderData.length allData allData.length isPressedLoadAll
  • fetch data without react-query and save data to one local state

No luck. FlatList data sometimes not updating.

Can someone explain what happening here?


Solution

  • I upgraded react-native version from 0.71.7 to 0.72.6. Looks like the problem has been fixed.