react-native 模仿原生 实现下拉刷新/上拉加载更多(RefreshListView)

时间:2020-12-29 05:50:43

1.下拉刷新/上拉加载更多 组件(RefreshListView)

src/components/RefreshListView/index.js

/**
* 下拉刷新/上拉加载更多 组件(RefreshListView)
*/
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
View,
Text,
StyleSheet,
FlatList,
ActivityIndicator,
TouchableOpacity,
ViewPropTypes,
RefreshControl
} from 'react-native' const RefreshState = {
Idle: 0,
HeaderRefreshing: 1,
FooterRefreshing: 2,
NoMoreData: 3,
Failure: 4,
EmptyData: 5,
} class RefreshListView extends PureComponent {
static propTypes = {
data: PropTypes.array.isRequired,
renderItem: PropTypes.func.isRequired,
refreshState: PropTypes.number.isRequired, listRef: PropTypes.node,
onHeaderRefresh: PropTypes.func,
footerContainerStyle: ViewPropTypes.style,
footerTextStyle: ViewPropTypes.style, disabledSeparator: PropTypes.bool,
disabledHeaderRefresh: PropTypes.bool,
footerRefreshingText: PropTypes.string,
footerFailureText: PropTypes.string,
footerNoMoreDataText: PropTypes.string,
footerEmptyDataText: PropTypes.string, ListEmptyComponent: PropTypes.node,
footerRefreshingComponent: PropTypes.node,
footerFailureComponent: PropTypes.node,
footerNoMoreDataComponent: PropTypes.node,
footerEmptyDataComponent: PropTypes.node,
} static defaultProps = {
disabledHeaderRefresh: false,
footerRefreshingText: '数据加载中…',
footerFailureText: '点击重新加载',
footerNoMoreDataText: '已加载全部数据',
footerEmptyDataText: '暂时没有相关数据',
} componentWillReceiveProps(nextProps) {} componentDidUpdate(prevProps, prevState) {} onHeaderRefresh = () => {
if (this.shouldStartHeaderRefreshing()) {
this.props.onHeaderRefresh(RefreshState.HeaderRefreshing)
}
} onEndReached = () => {
if (this.shouldStartFooterRefreshing()) {
this.props.onFooterRefresh && this.props.onFooterRefresh(RefreshState.FooterRefreshing)
}
} shouldStartHeaderRefreshing = () => {
if (this.props.refreshState == RefreshState.HeaderRefreshing || this.props.refreshState == RefreshState.FooterRefreshing) {
return false
}
return true
} shouldStartFooterRefreshing = () => {
const {refreshState, data} = this.props
if (data.length == 0) {
return false
}
return (refreshState == RefreshState.Idle)
} renderSeparator = () => (
<View style={{height: 1, backgroundColor: '#e0e0e0'}} />
) renderFooter = () => {
let footer = null let {
footerRefreshingText,
footerFailureText,
footerNoMoreDataText,
footerEmptyDataText, footerRefreshingComponent,
footerFailureComponent,
footerNoMoreDataComponent,
footerEmptyDataComponent,
} = this.props switch (this.props.refreshState) {
case RefreshState.Idle: {
footer = (<View style={styles.footerContainer} />)
break
}
case RefreshState.Failure: {
footer = (
<TouchableOpacity onPress={() => {
if (this.props.data.length == 0) {
this.props.onHeaderRefresh && this.props.onHeaderRefresh(RefreshState.HeaderRefreshing)
} else {
this.props.onFooterRefresh && this.props.onFooterRefresh(RefreshState.FooterRefreshing)
}
}}
>
{footerFailureComponent ? footerFailureComponent : (
<View style={styles.footerContainer}>
<Text style={styles.footerText}>{footerFailureText}</Text>
</View>
)}
</TouchableOpacity>
)
break
}
case RefreshState.EmptyData: {
footer = (
<TouchableOpacity onPress={() => {
this.props.onHeaderRefresh && this.props.onHeaderRefresh(RefreshState.HeaderRefreshing)
}}
>
{footerEmptyDataComponent ? footerEmptyDataComponent : (
<View style={styles.footerContainer}>
<Text style={styles.footerText}>{footerEmptyDataText}</Text>
</View>
)}
</TouchableOpacity>
)
break
}
case RefreshState.FooterRefreshing: {
footer = footerRefreshingComponent ? footerRefreshingComponent : (
<View style={styles.footerContainer} >
<ActivityIndicator size="small" color="#888888" />
<Text style={[styles.footerText, {marginLeft: 7}]}>{footerRefreshingText}</Text>
</View>
)
break
}
case RefreshState.NoMoreData: {
footer = footerNoMoreDataComponent ? footerNoMoreDataComponent : (
<View style={styles.footerContainer} >
<Text style={styles.footerText}>{footerNoMoreDataText}</Text>
</View>
)
break
}
}
return footer
} render() {
const {renderItem, ...rest} = this.props
return (
<FlatList
ref={this.props.listRef}
{...rest}
// 行与行之间的分隔线组件
ItemSeparatorComponent={this.props.disabledSeparator?false:this.renderSeparator}
// 列表为空时渲染该组件
ListEmptyComponent={this.props.ListEmptyComponent}
// 头部组件
ListHeaderComponent={this.props.renderHeader}
// 尾部组件
ListFooterComponent={this.renderFooter}
// 当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用
onEndReached={this.onEndReached}
// 刷新组件
refreshControl={
this.props.disabledHeaderRefresh?false:<RefreshControl
colors={['#00ff00',"#9Bd35A", "#689F38",]}
refreshing={this.props.refreshState == RefreshState.HeaderRefreshing}
onRefresh={this.onHeaderRefresh}
/>}
// 决定当距离内容最底部还有多远时触发onEndReached回调
onEndReachedThreshold={0.1}
// 根据行数据data,渲染每一行的组件
renderItem={renderItem}
/>
)
}
} const styles = StyleSheet.create({
footerContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
padding: 10,
height: 44,
},
footerText: {
fontSize: 14,
color: '#555555',
},
}) export {
RefreshState,
} export default RefreshListView;

2.页面调用

(1)定义全局变量

// 刷新状态
global.RefreshState = {
Idle: 0, // 加载成功
HeaderRefreshing: 1, // 开始下拉刷新
FooterRefreshing: 2, // 开始上拉翻页
NoMoreData: 3, // 加载全部数据
Failure: 4, // 加载失败
EmptyData: 5, // 服务器没有数据
}

(2)通用store

@observable
refreshState: any; /**
* 改变refreshState的值
* @param refreshState
*/
@action setRefreshState(refreshState) {
this.refreshState = refreshState
}

(3)当前 store

// 加载成功
this.setRefreshState(RefreshState.Idle); if(!res.data.topics.length){
// 服务器没有数据
this.setRefreshState(RefreshState.EmptyData);
}

(4)页面

const { data, refreshState, loadData, loadMoreData } = this.store;

// 新闻列表
store = new NewsStore(); // 子组件渲染
_renderRow(obj) {
let item = obj.item;
return (
<ListRow
key={item.id}
title={item.title}
onPress={() => {
// 跳转详情页
Actions.homeDetailPage({detail: item})
}}
/>
)
} <RefreshListView
data={toJS(data)}
keyExtractor={(item,index) => index.toString()}
renderItem={this._renderRow.bind(this)} refreshState={refreshState}
onHeaderRefresh={loadData.bind(this.store)}
onFooterRefresh={loadMoreData.bind(this.store)}
/>

3.效果图

react-native 模仿原生 实现下拉刷新/上拉加载更多(RefreshListView)