加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-15 20:24:37 所属栏目:百科 来源:网络整理
导读:1.下拉刷新/上拉加载更多 组件(RefreshListView) src/components/RefreshListView/index.js /** * 下拉刷新/上拉加载更多 组件(RefreshListView) */import React,{PureComponent} from ‘react‘;import PropTypes from ‘prop-types‘;import { View,Text

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.效果图

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读