问题描述: 使用antd mobile 的listview 组件展示列表数据,每个列表项除了展示数据还有一个checkbox,整个列表上方有一个控制checkbox是否显示的按钮,当点击“显示”后,checkbox显示出来,此时上滑列表“加载更多”数据,再点击“隐藏”checkbox,此时的checkbox不消失
解决方法: 需要重新设置dataSource,并且如果是在之前的数据列表的基础上添加的新数据不会导致更新,需要重新深拷贝一份新的数据,即:
this.setState({ dataSource: this.state.dataSource.cloneWithRows(JSON.parse(JSON.stringify(list))) })
讨论为什么在旧列表的基础上添加新数据不会更新视图:
切换按钮状态可以触发render方法,但是由于antd-mobile对性能的优化,并不会每次都触发renderRow方法。
关于listview渲染row的过程,需要参考antd mobile的ListView源码中v2.3.0版本中关于ListView的内容,和最重要的rmc-list-view源码,因为antd mobile的listview是在rmc-list-view的基础上封装的,如下所示为antd mobile中Listview的render: 其中的MListView组件导出自rmc-list-view ,在该组件中,关于row渲染的部分在ListView.js中如下所示。其中决定StaticRenderer 组件是否渲染的关键在于shouldUpdateRow 变量,根据其取值继续寻找可能导致row不更新的原因
在这里不去关注rowCount和this._prevRenderedRowsCount的关系(因为和我们这个问题无关),所以需要关注rowShouldUpdate() 方法的实现过程,实现过程如下所示。_dirtyRows ->needsUpdate 变量决定是否需要更新,事实上_dirtyRows 中的值正是决定是否更新的关键。
到此,转换思路,回到问题本身,如果我们设置dataSource的方式是this.setState({ dataSource: this.state.dataSource.cloneWithRows(list) }) ,可知导致问题的原因一定和cloneWithRows方法有关,因此需要了解cloneWithRows方法做了什么,如下,实际上它调用了另一个方法cloneWithRowsAndSections
在cloneWithRowsAndSections 方法中,如下,设置sectionIds和rowIds数组,并对数据进行脏检查,判断数据是否发生变化,设置_dirtyRows 的过程就在_calculateDirtyArrays 方法中 在_calculateDirtyArrays 方法中,判断每一个section中的每一个row是否发生变化,并将结果保存在_dirtyRows中。在判断row是否发生变化的过程中,一方面依据prevRowHash 这样一个类似于字典结构的对象来判断该sectionId(rowId)是否已经存在过,另一方面依据_rowHasChanged(也就是我们在构造函数中使用的rowHasChanged 方法)方法判断每一个row是否和原来相同,这个方法对我们传入的参数(通常是row1,row2)进行了封装,需要继续查看_getRowData方法 在ListViewDataSource的构造函数中(也就是我们代码中使用的new ListView.DataSource() )对_getRowData方法进行了初始化,默认使用自带的defaultGetRowData 方法,而在defaultGetRowData 中返回的值是我们给的数据的引用!而我们在list的基础上使用[…list,newdata]方法添加新数据时原本已有的每条数据的引用并没有发生变化(仅限对象数组)。所以当我们使用常用的new ListView.DataSource({rowHasChanged: (row1: any, row2: any) => row1 !== row2}) 方式来构造dataSource时,非新增数据的row1和row2的引用实际上是一样的,也就是列表没有更新的原因! 也就相当于rows=[{name:'11'}],rows2=[...rows,{name:'22'}],而rows[0]===rows2[0] 是一样的原理。
|