我正在做一个本机反应项目,用户可以使用Flickr API搜索图像,其他所有工作都很好,但是实现分页时遇到了问题。我已经使用FlatList的onEndReached
来检测用户何时滚动到列表的末尾,但是问题是onEndReached
被多次调用(包括第一次渲染期间被调用)。我here甚至禁用了弹跳功能,但仍被多次调用
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();
}
}
答案 0 :(得分:9)
此解决方案对我有用。在 FlatList 组件中添加 onMomentumScrollBegin 并修改 onEndReached 。
<FlatList
style = { ...}
data = {data}
initialNumToRender = {10}
onEndReachedThreshold = {0.1}
onMomentumScrollBegin = {() => {this.onEndReachedCalledDuringMomentum = false;}}
onEndReached = {() => {
if (!this.onEndReachedCalledDuringMomentum) {
this.retrieveMore(); // LOAD MORE DATA
this.onEndReachedCalledDuringMomentum = true;
}
}
}
/>
答案 1 :(得分:5)
最好使用onEndReached
设置布尔值true,然后基于此使用onMomentumScrollEnd
。
onEndReached={() => this.callOnScrollEnd = true}
onMomentumScrollEnd={() => {
this.callOnScrollEnd && this.props.onEndReached()
this.callOnScrollEnd = false
}
答案 2 :(得分:2)
多次触发onEndReached
的原因是因为您没有正确设置initialNumToRender
。
onEndReached
在VirtualizedList的_maybeCallOnEndReached中被触发。
_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)
) {
...
如果contentLength
(一次渲染的内容的长度)和visibleLength
(通常是屏幕的高度)接近,则distanceFromEnd
可能很小,因此distanceFromEnd < onEndReachedThreshold * visibleLength
可以始终为true
。通过设置initialNumToRender
并控制contentLength
的大小,可以避免不必要的onEndReached
调用。
这是一个例子。如果在初始渲染时渲染70像素单元格的10个项目(这是initialNumToRender
的默认道具),则contentLength
变为700。如果您使用的设备是iPhoneX
,则{{ 1}}是724。在这种情况下,visibleLength
是24,除非您将distanceFromEnd
设置为小于0.03,否则它将触发onEndReached
。
答案 3 :(得分:0)
您只需要将onEndReachedThreshold
设置为visibleLength的速率。
因此,您只需要将其设置为小于1的数字(例如0或0.5)就可以了!
让我知道这是否对您有用。
答案 4 :(得分:0)
这是我解决问题的方法:
这是我的初始状态:
state = {
onEndReachedCalledDuringMomentum: true,
lastLoadCount: 0,
}
这是我的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()}
/>
以下是我正在调用的函数:
// 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
)
}
一旦获得数据,就在_callTheAPIToFetchMoreData()
内部,我将像这样更新状态:
this.setState({
lastLoadCount: results.length,
onEndReachedCalledDuringMomentum: results.length >= 20 ? true : false,
notFinalLoad: results.length >= 20 ? true : false
}
快乐的编码。
答案 5 :(得分:-1)
onEndReached被调用了很多次,而使用onMomentumScrollEnd来加载惰性数据。
<FlatList
data={data}
keyExtractor={item => item.uid}
onMomentumScrollEnd={retrieveData}
onEndReachedThreshold={0}
/>
答案 6 :(得分:-1)
您可以在接收数据时禁用滚动。在FlatList中,在加载数据时设置scrollEnabled
false
。
scrollEnabled={!this.props.loadingData}