I have a simple component with list of Cards, what I want to do is also simple which is infinite scrolling, so that when I reach almost the end to just increment the page by 1 and call my API ( using RTK query here ) but the onEndReached have a very weird behaviour.
It get called like 10 times and when I check my API calls page is 2,4,5,6,8, 10... So it's like also skipping pages and it's calling these when I scroll ONLy one time to the end.
Here is my component
import { FlatList, StyleSheet, View } from 'react-native';
import React, { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { useGetNearbyPlacesQuery } from '@redux/queries/generalAPI';
import StoreCardSkeleton from '@components/StoreCard/CardSkeleton';
import StoreCard from '@components/StoreCard';
import { hp } from '@utils/responsive';
import ListLayout from '@components/ListLayout';
import { selectSessionLocation } from '@redux/reducers/authSlice';
const LIMIT = 10;
const Places = ({ route }) => {
const category = route?.params?.category;
const { t } = useTranslation();
const [page, setPage] = useState(1);
const sessionLocation = useSelector(selectSessionLocation);
const {
data = [],
isFetching = true,
refetch,
} = useGetNearbyPlacesQuery({
lat: sessionLocation?.[1],
lng: sessionLocation?.[0],
page,
limit: LIMIT,
type: category,
});
const renderItem = useCallback(
({ item }) => (
<View style={styles.item}>
<StoreCard
logo={item?.logo}
location={item?.buildingName}
typeOfSector={item?.typeOfSector}
storeId={item._id}
name={item?.vendorName}
/>
</View>
),
[]
);
const renderSkeletons = useCallback(() => {
if (!isFetching) {
return null;
}
const skeletonArray = Array.from({ length: 10 });
return skeletonArray.map((_, index) => (
<View style={styles.item} key={index}>
<StoreCardSkeleton />
</View>
));
}, []);
const handleRefresh = () => {
if (page > 1) {
setPage(1);
} else {
refetch();
}
};
const handleEndReached = useCallback(() => {
if (!isFetching) {
setPage((prev) => prev + 1);
}
}, [isFetching]);
return (
<ListLayout
title={t('nearby_places')}
onRefresh={handleRefresh}
renderLoading={renderSkeletons}
onEndReached={handleEndReached}
onEndReachedThreshold={0.2}
loading={isFetching}
data={data}
renderItem={renderItem}
keyExtractor={(item) => item?._id}
/>
);
};
export default Places;
const styles = StyleSheet.create({
item: {
marginBottom: hp(20),
},
});
and here is my Custom List Component
import {
RefreshControl,
StyleSheet,
FlatList,
ActivityIndicator,
View,
} from 'react-native';
import React from 'react';
import EmptyResult from '@components/EmptyResult';
import Colors from '@constants/Colors';
import { hp } from '@utils/responsive';
const List = ({
data = [],
keyExtractor,
renderItem = () => {},
listStyle,
loading = false,
renderLoading = () => {},
onEndReached = () => {},
onRefresh = () => {},
onEndReachedThreshold = 0.2,
page,
}) => {
const renderEmptyList = () => {
if (loading) {
return null;
}
return <EmptyResult />;
};
const defaultKeyExtractor = (item, index) => item._id + index.toString();
const handleLoading = () => {
if (page === 1) {
renderLoading();
} else if (loading) {
return (
<View style={styles.loadingWrapper}>
<ActivityIndicator size="small" color={Colors.RED} />
</View>
);
}
};
return (
<FlatList
data={data}
keyExtractor={keyExtractor || defaultKeyExtractor}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
contentContainerStyle={[styles.list, listStyle]}
onEndReachedThreshold={onEndReachedThreshold}
ListEmptyComponent={renderEmptyList}
ListFooterComponent={handleLoading}
onEndReached={onEndReached}
extraData={data}
refreshControl={
<RefreshControl
refreshing={loading}
onRefresh={onRefresh}
tintColor={Colors.RED}
colors={[Colors.RED]}
/>
}
/>
);
};
export default React.memo(List);
const styles = StyleSheet.create({
list: {
flexGrow: 1,
paddingBottom: hp(30),
},
loadingWrapper: {
alignContent: 'center',
justifyContent: 'center',
},
});
hey there once I also go through this kind of problem there are some problems in the problem is This can happen because the "onEndReached" event is triggered when the user scrolls to the end of the list, but the event might be fired multiple times as the scroll position stabilizes.
another way :- you can also use redux state for page number then you track your page number perfectly and remember that use "useFocusEffect" so when you leave that screen then you can reset your "pagenumber state". https://reactnavigation.org/docs/function-after-focusing-screen/
best of luck