React-Native开发中的灵异组件-列表
灵异组件
众所周知,列表是移动开发中非常常用的组件(控件)。原生的列表,拿iOS来来说,有UITableView & UICollectionview,就算是长列表的情况也只是加载速度慢,不会出现突然不再加载的情况,Android的应该也是这种情况。但是React-Native中提供的列表就会出现突然不再加载的情况,严格来说是列表的item不再渲染了,因为查看数据,发现列表的数据的数据是正确的。React-Native提供给开发者的列表组件有三种, FlatList
, SectionList
,ListView
,目前的使用情况来看,FlatList
& SectionList
都会出现这种灵异事件。
使用场景
国家手机号代码列表
由于项目的需要,目前的App需要提供一个全球的手机号代码列表,供用户注册的时候,选择自己国家的手机号代码。列表见下图:
这个列表的数据一共是242条,由于需要排序分组并且给用户提供索引,所以采用sectionList来实现该页面。页面刚完成的时候,效果还算可以,除了快速滑动的时候会出现白屏,索引的跳转不是很准确(我从网上查了一下,那两个是React-native本省的问题),算是完成任务,就提交给策划,然后进行新功能的开发。
实现代码如下:
render(){
return (<View style={styles.containStyle}>
<CustomNavBar title="Country" navigator={this.props.navigator}></CustomNavBar>
<SectionList ref={el=> this.sectionList=el}
initialNumToRender={20}
showsVerticalScrollIndicator={true}
// onEndReachedThreshold={0.1}
// onEndReached={()=> this.loadMoreData(false)}
sections={this.state.totalSections}
renderItem={this._renderItem}
getItemLayout={this._getItemLayout}
keyExtractor={(item: Item, index: number)=> index.toString()}
extraData={this.state}
renderSectionHeader={this._renderSectionHeader}
ItemSeparatorComponent={()=><View style={{width: '100%', height: 1/PixelRatio.get(), backgroundColor: THEME_COLOR.SEPATOR_COLOR, marginLeft: 12, marginRight: 24}}></View>}>
</SectionList>
<SideBar indexs={this.state.indexs} onLetterSelectedListener={this.onSideBarSelected.bind(this)}></SideBar>
</View>);
}
_renderItem = (info: any) => {
return (<CountryCodeItem item={info.item} itemSelected={this.itemSelected.bind(this, info.item)}></CountryCodeItem>);
}
_getItemLayout = (data:any, index:number) => {
const itemHeight = 40;
return {
length: itemHeight,
offset: (itemHeight + 0) * index,
index,
};
}
_renderSectionHeader = (info: any) => {
let txt = info.section.key;
return <Text key={txt} style={{ height:20, textAlignVertical: 'center', backgroundColor: THEME_COLOR.BOX_BOTTOM_COLOR, color: '#666666', fontSize: 12, paddingLeft: 16, paddingTop: 4}}>{txt}</Text>
}
itemSelected(item: Item){
console.log('the current item is===='+JSON.stringify(item));
const result = item.countryCode + ' ' + item.phoneCode;
if(this.props.updatePhoneCode){
this.props.updatePhoneCode(result, item.phoneCode);
this.props.navigator.pop();
}
}
onSideBarSelected(letter:string){
let sections: sectionData[] = this.state.totalSections;
if (sections) {
for (let index = 0; index < sections.length; index++) {
let item = sections[index];
if (item.key === letter) {
console.log('the current index i ==' + index);
this.sectionList.scrollToLocation({animated: true, itemIndex: 0, sectionIndex: index, viewPosition: 0.0});
break;
}
}
}
}
好友列表
好友列表,好友列表的效果和上面一样,也是需要分组和索引,由于好友列表开发的比较早,我是采用的FlatList
实现的功能。
代码如下:
renderSuccessView(){
return (
<View style={styles.container}>
<View style={styles.content}>
<FlatList
ref={el=> this.list = el}
data={this.state.listData}
extraData={this.state}
keyExtractor={(item: any, index: number)=> index.toString()}
renderItem={this._renderItem}
getItemLayout={this._getItemLayout}
ListEmptyComponent={this.renderEmptyView}
ItemSeparatorComponent={this.itemSeparator}
initialNumToRender={20}
//onEndReachedThreshold={0.8}
//legacyImplementation={this.state.listData.length > 0}
showsVerticalScrollIndicator={true}
/>
<View style={{position: 'absolute', height: '100%', width: 15, right: 0, backgroundColor: 'transparent', justifyContent: 'center', alignItems: 'center'}}><SideBar indexs={this.indexs} onLetterSelectedListener={this.onSideBarSelected.bind(this)}/></View>
</View>
<View style={styles.divider}></View>
</View>
);
}
_renderItem = (item:any) => {
console.log('the current item-->' + JSON.stringify(item)+" key: "+ item.item.key);
return (<FriendListItem item={item.item} spreadValue={this.state.scaleValue} onListItemClick={this.onListItemClick.bind(this)}
acceptNewFriend={this.acceptNewFriend.bind(this)}
newFriendsSectionUnfold={this.newFriendsSectionUnfold.bind(this)}>
</FriendListItem>);
}
好友列表刚开始做的时候bug比较多,经过几次修改,修复了出现的那些bug,效果也打到达了策划的要求。本以为任务完成了,还算完美。但是在接下来的反复测试中,灵异事件出现了。
灵异事件
在测试的过程中,出现了好友列表只加载10条的情况,10是initialNumToRender
设置第一屏渲染的数据条数。而且很奇怪,好友列表第一个固定的分组是好友的申请记录,其余的显示已经是好友的数据,按拼音的首字母进行分组排序。好友申请记录的显示逻辑是一开始最多显示5条,点击展开按钮,在全部显示出来。
好友列表的灵异事件刚开始发现的时候,好友申请记录显示了5条,已经是好友的数据显示了5条,当点击了申请记录的展开按钮,申请记录是6条,已经是好友的数据就少了最后一条,变成了4条,但是把申请收齐,好友数据又成了5条了。
刚开始以为是之前的bug没有改彻底,之前因为因为有key重复的情况,出现了数据缺少的情况,经过反复review代码,发现不是那个bug。反复进入好友列表,发现只渲染10条的灵异事件,低端机上比较容易出现,比如iphone5S,Android的低端机上也比较容易出现,高端机上出现的频率很低。
于是去测试国家编码列表,那个列表一共是242条数据,也是右边有分组索引,也出现了同样的问题。
而且出现的时候列表的可滑动区域变得很大,打印数据源,发现数据源是正常的,只是屏幕上只渲染了initialNumToRender设置的条数,就算你设置了2,也只显示2条,设置一个很大的值,也能渲染,就是渲染慢,要等待,数据条数越多,时间越长。
网上查了很多资料,尝试了很多解决办法。
- 1.分页加载数据。
- 2.列表的Item继承PureComponent。
- 3.使用ListView替代。
分页加载
分页加载是网上查找到的,说是FlatList
& SectionList
提供了分页加载的触发方法,当一屏数据快要滑动到底部的时候,触发一个方法,可以再去加载另外一部分数据。于是乎赶紧实现了一把,数据也是分页加载的,经过反复测试,发现分页加载的情况下,也会出现不再渲染的情况。而且因为列表右边有索引,分页加载其实也不能满足需求,本想着分页加载能够解决灵异事件,去和策划沟通一下,索引的功能稍微改一改,结果发现分页加载的思路也行不通。
列表的Item继承PureComponent
网上查到一份资料,有人说是把列表的item的直接继承PureComponent
(之前继承的是React.Component
),于是满怀希望赶紧把item重新改写了一下,最后发现然并卵,那个灵异效果依旧会出现。
使用ListView替代
使用ListView
替代,查了一下关芳芳文档,说是ListView
本身的性能不好,才重新封装了
FlatList
& SectionList
来替代它。用ListView
替代以后,列表能够完全渲染了,就是有点慢,而且官方文档说ListView
在超出屏幕之外,并没有做回收,数据量打了,终究是个隐患。内存暴涨,滑动卡顿。
其实在测试的过程中,发现如果直接把 列表的initialNumToRender设置成列表数据的长度,也能暂时解决问题,但是数据量大了,加载会很慢。拿国家编码列表来举例,242条数据,大概需要卡住5s左右,随着数据量的增大,效果可想而知,好友列表的数据容纳量目标是1500条,瞬间就泪崩了。
总结
目前来说,经过网上查询资料,自己尝试,都木有彻底解决React-Native列表的灵异问题,目前的解决方案,只是暂时性,数据量大了,问题会很明显,希望遇到过同样问题的同学,能够一起讨论解决,共同成长,或者有解决方案,麻烦告知一声,谢谢了。