《React-Native系列》19、 ListView组件之上拉刷新(iOS和Android通用)

ReactNative提供了RefreshControl下拉刷新组件,但是没有提供上拉刷新组件,上拉刷新在App中是很常用的。

今天我们来实现一个iOS和Android通用的上拉刷新功能。

下面简要介绍下我实现的思路。

如果你对ListView的基础知识不是很清楚,建议先移步:《React-Native系列》16、 RN组件之ListView


思路:

1、常量定义:

const moreText = "加载完毕";    //foot显示的文案
//页码
var pageNum = 1;
//每页显示数据的条数
const pageSize = 10;
//页面总数据数
var pageCount = 0;
//页面List总数据
var totalList = new Array();

//foot:  0 隐藏  1  已加载完成   2  显示加载中

2、定义ListView

<ListView
    enableEmptySections={true}
    dataSource={this.state.dataSource}
    renderRow={this._renderRow.bind(this)}
    renderFooter={this._renderFooter.bind(this)}
    onEndReached={this._endReached.bind(this)}
    onEndReachedThreshold={0}
/>



3、声明State状态机变量

ListView.DataSource实例(列表依赖的数据源)

  constructor(props) {
      super(props);
      this.state = {
          dataSource: new ListView.DataSource({
              rowHasChanged: (r1, r2) => r1 !== r2,
          }),
         loaded: false,//控制Request请求是否加载完毕
         foot:0,// 控制foot, 0:隐藏foot  1:已加载完成   2 :显示加载中
         error:false,
}

这里我们主要声明了dataSource,这个没什么说的

loaded:用来控制整个页面的菊花

error:如果Request错误,显示一个错误页面

foot: 控制Footer的view


4、渲染页面前,加载数据

  componentWillMount() {
      this._fetchListData();
  }

5、Load服务端数据

  _fetchListData() {
      if(pageNum > 1){
        this.setState({loaded:true});
      }
      fetch(requestURL, {
          method: 'get',
          headers: headerObj,
      }).then(response =>{
        if (response.ok) {
            return response.json();
        } else {
            this.setState({error:true,loaded:true});
        }
      }).then(json=>{
          let responseCode = json.code;
          if (responseCode == 0) {
              let responseData = json.data;

              pageCount = responseData.count;
              let list = responseData.data;

              if (orderList == null) {
                  orderList = [];
                  currentCount = 0;
              } else {
                  currentCount = list.length;
              }
              if(currentCount < pageSize){
                //当当前返回的数据小于PageSize时,认为已加载完毕
                this.setState({ foot:1,moreText:moreText});
              }else{//设置foot 隐藏Footer 
                this.setState({foot:0});
              }
              for (var i=0; i < list.length; i++) {
                totalList.push( list[i] );
              }
 
              this.setState({
                  dataSource: this.state.dataSource.cloneWithRows(totalList),
                  loaded: true,
              });
          }else{
              this.setState({error:true,loaded:true});
          }
      }).catch(function (error) {
          this.setState({error:true,loaded:true});
      });
  }

这里的细节挺多的:

1、当pageNum > 1时,就不要整个页面的菊花,此时loaded一直为true,这个主要是为了页面效果,要不然没加载一页数据,这个屏幕就会闪一下。

2、比较当前返回的list的大小,是否小于pageSize,控制Footer是否隐藏,还是显示已加载完毕

3、声明了一个全局的totalList对象,每次有新数据的时候,都push进去。

如果不采用push的方式的话,直接采用setState方法的话,第二页会把第一页的数据覆盖掉。


6、定义renderRow方法

renderRow={this._renderRow.bind(this)}   列表组件渲染函数 ,此处页面逻辑省略。


7、定义renderFooter方法

renderFooter   页脚会在每次渲染过程中都重新渲染。

  _renderFooter() {
      if(this.state.foot === 1){//加载完毕
        return (
          <View style={{height:40,alignItems:'center',justifyContent:'flex-start',}}>
              <Text style={{color:'#999999',fontSize:12,marginTop:10}}>
                  {this.state.moreText}
              </Text>
          </View>);
      }else if(this.state.foot === 2) {//加载中
        return (
          <View style={{height:40,alignItems:'center',justifyContent:'center',}}>
            <Image source={{uri:loadgif}} style={{width:20,height:20}}/>
          </View>);
      }
  }

根据状态机变量foot控制Footer的显示



8、onEndReached 定义

onEndReachedThreshold={0}

当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的),这个事件也会被触发

  _endReached(){
    if(this.state.foot != 0 ){
      return ;
    }
    this.setState({
      foot:2,
    });
    this.timer = setTimeout(
      () => {
        pageNum ++;
        this._fetchListData();
      },500);
  }

这里需要注意一下几点

1、第一屏的时候可能也会触发_endReached方法,所以需要判断foot为非 0(即加载中和已加载完毕)时,直接return

2、上拉时,触发_endReached方法,可能server端接口响应很快,几乎看不到菊花效果,特地加了个500毫秒的等待 


9、卸载Timer

  componentWillUnmount() {
  // 如果存在this.timer,则使用clearTimeout清空。
  // 如果你使用多个timer,那么用多个变量,或者用个数组来保存引用,然后逐个clear
    this.timer && clearTimeout(this.timer);
  }



如果有问题疑问,可以留言交流。


  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值