您的位置:首页 > Web前端 > React

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

2016-12-18 17:06 1371 查看
ReactNative提供了RefreshControl下拉刷新组件,但是没有提供上拉刷新组件,上拉刷新在App中是很常用的。

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

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

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

思路:

1、常量定义:

[javascript] view
plain copy

 





const moreText = "加载完毕";    //foot显示的文案  

//页码  

var pageNum = 1;  

//每页显示数据的条数  

const pageSize = 10;  

//页面总数据数  

var pageCount = 0;  

//页面List总数据  

var totalList = new Array();  

  

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

2、定义ListView

[javascript] view
plain copy

 





<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实例(列表依赖的数据源)

[javascript] view
plain copy

 





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、渲染页面前,加载数据

[javascript] view
plain copy

 





componentWillMount() {  

    this._fetchListData();  

}  

5、Load服务端数据

[javascript] view
plain copy

 





_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   页脚会在每次渲染过程中都重新渲染。

[javascript] view
plain copy

 





_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个像素的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的),这个事件也会被触发

[javascript] view
plain copy

 





_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

[javascript] view
plain copy

 





componentWillUnmount() {  

// 如果存在this.timer,则使用clearTimeout清空。  

// 如果你使用多个timer,那么用多个变量,或者用个数组来保存引用,然后逐个clear  

  this.timer && clearTimeout(this.timer);  

}  

待优化点:

1、如果ListView的翻页较多的话,全局变量totalList会非常大,对内存是考验。

如果有问题疑问,可以留言交流。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐