问题
I'm making a react native project where user can search images using Flickr API, Everything else is working fine but the problem i'm having while implementing pagination. I have used FlatList's onEndReached
to detect when user has scrolled to the end on the list, but the problem is onEndReached
is being called multiple times(including one during the first render). I have even disabled bounce as said here but it's still being called more than once
export default class BrowserHome extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
tagParam: "cat",
pageNum: -1,
data: [],
photosObj: ""
};
}
componentDidMount() {
this.setState({
isLoading: true
});
try {
this.makeRequest();
} catch {
console.log("error has occurred");
}
}
makeRequest = () => {
const { tagParam, pageNum } = this.state;
let url = `https://api.flickr.com/services/rest/?
method=flickr.photos.search
&api_key=${apiKey}&format=json&tags=${tagParam}
&per_page=30&page=${pageNum + 1}&nojsoncallback=1`;
fetch(url, {
method: "GET"
})
.then(response => response.json())
.then(responseJSON => {
this.setState({
data: this.state.data.concat(responseJSON.photos.photo),
isLoading: false,
pageNum: responseJSON.photos.page
});
})
.catch(error => {
console.log(error);
this.setState({ isLoading: false });
throw error;
});
};
render() {
if (this.state.isLoading) {
return <ActivityIndicator animating={true} size="large" />;
}
return (
<View
style={{
flex: 1,
height: 200,
justifyContent: "flex-start",
width: screenSize.width,
backgroundColor: "black"
}}
>
<Text>This is browserhome</Text>
<FlatList
style={{
width: screenSize.width
}}
numColumns={3}
data={this.state.data}
keyExtractor={item => item.id}
bounces={false}
onEndReachedThreshold={1}
onEndReached={({ distanceFromEnd }) => {
this.loadMoreItem();
alert("end reached call");
}}
renderItem={({ item, index }) => (
<>
<ImageTile imageURL={this.createImageURL(item)} />
// <Text style={{ color: "white" }}>
// {index}
// {console.log(index)}
// </Text>
</>
)}
/>
</View>
);
}
createImageURL(item) {
let server = item.server,
id = item.id,
secret = item.secret;
let urlString = `https://farm${
item.farm
}.staticflickr.com/${server}/${id}_${secret}_s.jpg`;
return urlString;
}
loadMoreItem() {
this.makeRequest();
}
}
回答1:
Here is how I solved my problem:
Here is my initial state:
state = {
onEndReachedCalledDuringMomentum: true,
lastLoadCount: 0,
}
This is my FlatList
<FlatList
keyboardShouldPersistTaps="always"
style={...}
data={this.state.searchResults}
extraData={this.state}
bounces={false}
renderItem={({ item, index }) =>
<SearchResultView
uriSsource={item.image}
itemIndex={index}
name={item.name}
/>
}
showsVerticalScrollIndicator={false}
keyExtractor={this._keyExtractor}
numColumns={2}
onEndReached={() => this._loadMoreData()}
onEndReachedThreshold={0.01}
ListFooterComponent={this._renderSearchResultsFooter}
onMomentumScrollBegin={() => this._onMomentumScrollBegin()}
/>
Here are the functions I am calling:
// Key Extractor
_keyExtractor = (item, index) => item.id;
// Check if list has started scrolling
_onMomentumScrollBegin = () => this.setState({ onEndReachedCalledDuringMomentum: false });
// Load more data function
_loadMoreData = () => {
if (!this.state.onEndReachedCalledDuringMomentum) {
this.setState({ onEndReachedCalledDuringMomentum: true }, () => {
setTimeout(() => {
if (this.state.lastLoadCount >= 20 && this.state.notFinalLoad) {
this.setState({
page: this.state.page + 1,
}, () => {
// Then we fetch more data;
this._callTheAPIToFetchMoreData();
});
};
}, 1500);
});
};
};
// Show your spinner
_renderSearchResultsFooter = () => {
return (
(this.state.onEndReachedCalledDuringMomentum && this.state.lastLoadCount >= 20 && this.state.notFinalLoad) ?
<View style={{ marginBottom: 30, marginTop: -50, alignItems: 'center' }}>
<ActivityIndicator size="large" color="#e83628" />
</View> : null
)
}
Once I get data, inside of _callTheAPIToFetchMoreData()
, I update the state like this:
this.setState({
lastLoadCount: results.length,
onEndReachedCalledDuringMomentum: results.length >= 20 ? true : false,
notFinalLoad: results.length >= 20 ? true : false
}
Happy coding.
回答2:
The reason of triggering onEndReached
multiple times is because you have not set initialNumToRender
properly.
onEndReached
is triggered in this _maybeCallOnEndReached in VirtualizedList.
_maybeCallOnEndReached() {
const {
data,
getItemCount,
onEndReached,
onEndReachedThreshold,
} = this.props;
const {contentLength, visibleLength, offset} = this._scrollMetrics;
const distanceFromEnd = contentLength - visibleLength - offset;
if (
onEndReached &&
this.state.last === getItemCount(data) - 1 &&
distanceFromEnd < onEndReachedThreshold * visibleLength &&
(this._hasDataChangedSinceEndReached ||
this._scrollMetrics.contentLength !== this._sentEndForContentLength)
) {
...
if contentLength
(the length of content rendered at once) and visibleLength
(usually the screen height) is close, distanceFromEnd
can be very small, thus distanceFromEnd < onEndReachedThreshold * visibleLength
can always be true
. By setting initialNumToRender
and control the size of contentLength
, you can avoid unnecessary onEndReached
call.
Here's an example. If you render 10 items(this is the default props of initialNumToRender
) of 70 px cells at the initial render, contentLength
becomes 700. If the device you are using is iPhoneX
then visibleLength
is 724. In that case distanceFromEnd
is 24 and this will trigger onEndReached
unless you set onEndReachedThreshold
less than 0.03.
回答3:
You just need to set onEndReachedThreshold
as a rate of visibleLength.
So you just need to set it as a number smaller than 1. ZERO for example or 0.5 then it should work!!!!!
Let me know if that worked for you.
回答4:
onEndReached is called much times, use onMomentumScrollEnd instead to load lazy data.
<FlatList
data={data}
keyExtractor={item => item.uid}
onMomentumScrollEnd={retrieveData}
onEndReachedThreshold={0}
/>
回答5:
you can disable scrolling while data receiving. in FlatList set scrollEnabled
false
while data loading.
scrollEnabled={!this.props.loadingData}
来源:https://stackoverflow.com/questions/53408470/flatlist-onendreached-being-called-multiple-times