1.下拉刷新/上拉加載更多 組件(RefreshListView)javascript
src/components/RefreshListView/index.jsjava
/** * 下拉刷新/上拉加載更多 組件(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.頁面調用node
(1)定義全局變量react
// 刷新狀態 global.RefreshState = { Idle: 0, // 加載成功 HeaderRefreshing: 1, // 開始下拉刷新 FooterRefreshing: 2, // 開始上拉翻頁 NoMoreData: 3, // 加載所有數據 Failure: 4, // 加載失敗 EmptyData: 5, // 服務器沒有數據 }
(2)通用storereact-native
@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)頁面flex
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.效果圖ui