1.本文前言
其实在现实各种常规业务中会遇到列表数据展示的页面需求开发,同时也会遇到各种各样的问题。
说到现实业务,可以举的例子有房产中介网中房源数据列表,线上购物中的商品列表等等业务。数据库中的产品或者商品数据成千上万条,全部加载起来的话无论是缓存资源还是终端资源肯定是吃不消的,一般情况下前端会对这些列表数据进行分页展示。
并且交互上为了更好的展示列表数据,业务或者产品经理会对数据打上标签进行分文别类。以便用户更快更便捷的寻找到想要的信息。
2.实践中遇到的问题
实际业务上开发列表页我也会遇到一些问题。最实际的例子就是,每一条数据都打上了标签,并且后端接口已经提供参数分类。用户在列表页中不停滴切换标签类型,而后端接口请求的是异步的,那么等到接口返回的时候列表展示的数据并不是用户需要的数据。这个时候前端要处理好这个交互数据问题。
3.代码中的实践
//页面展示组件
export default DataListComponent extend Component {
const tagList = ['fish','dog','cat']; // 数据标签
this.state = {
displayTag: 'fish', //展示标签
dataTag: 'fish', //数据标签
status: 'loading', //请求状态
dataList: [], //数据列表
}
// 切换标签的交互方法
onChangeTag(newTag){
if(this.state.status === 'loading'){
return;
}
this.setState({ displayTag: newTag, status:'loading'}, this.updateProductList);
}
//异步请求接口数据的方法
updateProductList(){
const { displayTag,dataTag } = this.state;
//请求数据接口的封装
DataApi.requestDataList(dataTag)
.then(res => {
//数据标签和前端展示标签不一致则修改数据标签重新请求
if(displayTag !== dataTag){
this.setState({ dataTag: displayTag, status:'loading'}, this.updateProductList);
}
else {
this.setState({dataList: res, status:'success'})
}
})
.catch(err => {
this.setState({dataList: [], status:'success'})
})
}
//渲染组件
render():{
const { dataList,status,displayTag } = this.state;
return (
<View>
<View className={styles.dataType}>
{tagList.map(type=>{
//用户可点击的标签按钮
return <Button value={type} onClick={this.onChangeTag(type)} highLight={displayTag === type} />
})}
</View>
<View className={styles.dataPart}>
{dataList.map(item=>{
//渲染产品卡片数据,此次省略此方法实现
return this.renderDataItem(item);
})}
</View>
<Loading status={status} />
<View>
)
}
}
这段列表代码考虑到数据异步处理并用了逻辑锁后实现起来天衣无缝,实际上运行的时候会有一个问题所在。
用户在不停的切换数据标签的时候没有考虑到数据接口请求的时间问题。就拿上述代码的数据标签举例。当用户选择'fish'标签时,接口等待接口返回数据又切换成'dog'标签。如果'dog'接口返回时长小于'fish'标签时,数据列表本来是'dog'的数据又会切换成'fish'的数据,看似极低概率的事情却会造成灾难性的后果。那么我们应该考虑一下更优的解决方案。
4.优化后的解决方案代码实现
我们考虑到异步接口请求的时长问题会导致列表数据于标签不一致,那么我们在用户切换数据标签的时候,应该直接切断上一个接口的请求。
以下是优化代码的实现:
onChangeTag(newTag){
if(this.state.status === 'loading'){
return;
}
this.setState({ displayTag: newTag, status:'loading'},
(): void => {
//如果接口正在请求,则删除上一个请求
if (this.cancelSource !== undefined) {
this.cancelSource.cancel();
this.cancelSource = undefined;
}
//重新定义新的接口请求删除source
source = axios.CancelToken.source();
this.cancelSource = source;
this.updateProductList();
}
);
}
// 接口请求也要重新定义,当整个请求结束时需要给接口请求删除source定义为undefined
updateProductList(){
const { displayTag,dataTag } = this.state;
DataApi.requestDataList(dataTag)
.then(res => {
this.cancelSource = undefined;
this.setState({dataList: res, status:'success'})
})
.catch(err => {
this.cancelSource = undefined;
this.setState({dataList: [], status:'success'})
})
}
5.结语
在大部分数据列表页面中都有可能遇到数据标签切换时列表数据与标签不一致的问题,总体解决思路还是需要取消上一个接口请求再请求正确的数据。