I am using state
to store the following data.
state = {
refresh: true,
isFetching: true,
showLoadingBottom: false,
data: []
};
on componentDidMount
I manually call a function _fetchData
which loads data into this.state.data
.
When the flatlist is scrolled to the end it fires _fetchData
twice which ends up returning the same data twice ( which is another problem, why does it fire twice? ).
Once the flatlist reaches the end ie, no more data is returned from the server, it goes into an endless loop since the onEndReached
continuously fires over and over again even though no new data is returned from the server and this.state.data
remains the same.
This is my render
code
render() {
return (
<View
style={{
flex: 1
}}>
<FlatList
refreshControl={
<RefreshControl
refreshing={this.state.refresh}
onRefresh={() => {
this.setState({
refresh: true
}, this._fetchData);
}}
title={"Pull To Refresh"}
tintColor={darkGrey}
titleColor={darkGrey}/>
}
onEndReachedThreshold={0.5}
onEndReached={() => {
this.setState({
showLoadingBottom: true
}, () => {
this._fetchData();
});
}}
showsVerticalScrollIndicator={false}
data={this.state.data}
ListFooterComponent={() => {
return (
this.state.showLoadingBottom &&
<View style={{padding: 10}}>
<ActivityIndicator size="small" color={colorAccent}/>
</View>
);
}}
renderItem={this._renderItem}
keyExtractor={(item) => item.id.toString()}
removeClippedSubviews={true}
/>
</View>
);
}
Here is my solution that can maybe be changed to suit other peoples needs:
Basically the important parts are onEndReached={this.state.moreData && this.retrieveMore}
. So you can test inside your onEndReached
function weather there is anymore data (In my case if we only return 1 object i know it's finished) then set state this.state.moreData
to false.
<SafeAreaView style={styles.container}>
<FlatList
data={Object.values(this.state.documentData)}
// Render Items
renderItem={({ item }) => (
<ItemSelector
item={item}
onPress={() => {this.selectItem(item)}}
/>
)}
// On End Reached (Takes in a function)
onEndReached={this.state.moreData && this.retrieveMore}
// How Close To The End Of List Until Next Data Request Is Made
onEndReachedThreshold={1}
ListEmptyComponent={
<Text>No jobs to show</Text>
}
/>
</SafeAreaView>
retrieveMore = async () => {
try {
// Set State: Refreshing
this._isMounted && this.setState({ refreshing: true });
fbDb.ref('job')
.orderByKey()
.startAt(this.state.lastVisible) //Start at the last item we found
.limitToFirst(this.state.limit) //Limit queries returned per page
.once('value', snapshot => {
//check if we got a result
if(snapshot.numChildren() > 1){
.....
this._isMounted && this.setState({
documentData: newstate,
lastVisible: lastVisible,
refreshing: false, //Hide loading icon
});
} else {
this._isMounted && this.setState({
refreshing: false, //Hide loading icon
moreData: false
});
}
});
}
catch (error) {
console.log(error);
}
};
I have a similar problem. In my case it is because the ListFooterComponent
.
If you render the ListFooterComponent
with this pattern or equivalent
onEndReachedThreshold={x} // for any x >= 0
ListFooterComponent={() => {
if (isDataFetching) {
return <SomeComponentWithSomeHeight />
} else {
return undefined;
}
}}
It will trigger onEndReached
infinitely when the user scrolls down the end of the list (or if your content is not longer than the list's visibility area).
And it is because the presence and absence of the <SomeComponentWithSomeHeight />
affects the height of the content and thus triggers the endReached
re-calculation.
And following are the possible solution I can think of.
Use negative onEndReachedThreshold
that is always "higher" than the height of the ListFooterComponent
. But I don't like this solution because it is difficult to know the "higher" (it is relative to the FlatList's visibility area). And the negative onEndReachedThreshold
may cause some issue on Android.
Implement your own loading overlay outside of the FlatList
so that the loading component does not affect the content height.
Set opacity = 0
to hide the ListFooterComponent
instead of returning undefined
, so that it is always there and the content height does not change when it becomes visible.
ListFooterComponent={() => {
return (
<View style={{ opacity: isDataFetching ? 1 : 0 }}>
<SomeComponentWithSomeHeight />
</View>
);
}}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With