方法一简单介绍下未嵌套
ScrollView
的FlatList
上拉加载应用场景,可自行忽略,直接跳至方法二嵌套scrollView
场景。
一、FlatList未嵌套在ScrollView的应用场景
1、最简单的方法是应用react-native-refresh-list-view
组件实现,其基于FlatList的二次封装。
通过控制RefreshState
来实现下拉刷新或者上拉加载。
export const RefreshState = {
Idle: 0,
HeaderRefreshing: 1,
FooterRefreshing: 2,
NoMoreData: 3,
Failure: 4,
EmptyData: 5,
}
具体应用的部分代码如下:
import RefreshListView, { RefreshState } from "react-native-refresh-list-view";
renderRefreshList = () => {
const { sourceData, refreshState } = this.state;
return (
<RefreshListView
data={sourceData}
ItemSeparatorComponent={() => this.renderSeparator()}
keyExtractor={(item, idx) => idx.toString()}
renderItem={({ item }) => this.renderCell(item)}
ListHeaderComponent={this.renderHeader}
refreshState={refreshState}
onHeaderRefresh={this.onHeaderRefresh}
onFooterRefresh={() => this.onCheckMore()}
footerNoMoreDataText="已经到底了"
/>
);
};
2、如果不想使用方法1
中的第三方组件,可以自定义上拉加载的组件
核心属性: onEndReachedThreshold
和onEndReached
当onEndReachedThreshold
设置大于1时,的确进入页面就触发,设置在0-1之间时按正常逻辑走。
<FlatList
...
onEndReachedThreshold={0.5}
...
/>
上拉加载更多onReached
被触发两次,造成重复请求资源,性能浪费
<FlatList
...
onEndReached={() => {
if (this.canLoadMore) {
this.loadData(true); //
this.canLoadMore = false;
}
}}
onEndReachedThreshold={0.5}
onMomentumScrollBegin={() => {
this.canLoadMore = true; //初始化时调用onEndReached的loadMore
}}
...
/>
通常情况下是先调用onMomentumScrollBegin
,然后调用onEndReached
,但是可能会存在意外情况
<FlatList
...
onEndReached={() => {
setTimeout(() => {
if (this.canLoadMore) {
this.loadData(true);
this.canLoadMore = false;
}
}, 100)
}}
onEndReachedThreshold={0.5}
onMomentumScrollBegin={() => {
this.canLoadMore = true; //初始化时调用onEndReached的loadMore
}}
...
/>
二、FlatList嵌套在ScrollView的应用场景
当FlatList
(或ListView
)和其他子组件在外面加了一个ScrollView
时,debugg
发现在一直不停地触发onEndReached
函数,也就是说父组件ScrollView
能滚动,导致FlatList
(或ListView
)中的数据不能满屏,于是就不停地触发该函数,不停fetch
请求。
还有,当FlatList
(或ListView
)的dataSource
第一次渲染时,如果为空,也会触发onEndReached
函数。
解决方案: 针对此问题,直接对最外层的scrollview
实现上拉加载功能
renderScrollView = () => {
const { isRefreshing } = this.state;
return (
<ScrollView
onScroll={event => this.onScroll(event)}
scrollEventThrottle={1}
onMomentumScrollEnd={this.handleScrollEnd}
refreshControl={
<RefreshControl
tintColor="#FFFFFF"
refreshing={isRefreshing}
onRefresh={() => this.onRefresh()}
/>
}
>
{this.renderView()}
</ScrollView>
);
};
// 判断上拉加载的时机
handleScrollEnd = event => {
const contentHeight = event.nativeEvent.contentSize.height;
const scrollViewHeight = event.nativeEvent.layoutMeasurement.height;
const scrollOffset = event.nativeEvent.contentOffset.y;
// 是否滑动到底部
const isEndReached = scrollOffset + scrollViewHeight >= contentHeight;
// 内容高度是否大于列表高度
const isContentFillPage = contentHeight >= scrollViewHeight;
const { reqSsqData, reqJpqData, reqWbqData, reqInData } = this.state;
if (isContentFillPage && isEndReached) {
// 已滑动scrollview底部,触发加载分页请求
}
};
需要注意:
在判断上拉加载的时机时,主要是比较以下2个时机,根据自己的需求添加
// 滚动结束的回调
onMomentumScrollEnd={this.handleScrollEnd}
// 手指松开屏幕的回调
onScrollEndDrag={this.handleScrollEnd}
参考:
https://www.jianshu.com/p/33ec6ceeb638
https://www.cnblogs.com/fe-linjin/p/10587720.html
https://blog.csdn.net/qq_21478985/article/details/80931386