解决antd mobile的长列表ListView不更新视图问题及源码浅析

问题描述:
使用antd mobile 的listview 组件展示列表数据,每个列表项除了展示数据还有一个checkbox,整个列表上方有一个控制checkbox是否显示的按钮,当点击“显示”后,checkbox显示出来,此时上滑列表“加载更多”数据,再点击“隐藏”checkbox,此时的checkbox不消失

解决方法:
需要重新设置dataSource,并且如果是在之前的数据列表的基础上添加的新数据不会导致更新,需要重新深拷贝一份新的数据,即:

//如果是list=[...list,newdata],则需要深拷贝
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]是一样的原理。
在这里插入图片描述在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Ant Design 中,一个表单(Form)只能有一个包含所有表单项的 Form.Item,因此无法直接嵌套多个 Form。但是,可以通过使用表单域组(Form.List)来实现套嵌表单。 具体实现步骤如下: 1. 在外层表单(父表单)中使用 Form.List 包裹需要嵌套的表单项,例如: ```jsx <Form.List name="nestedForm"> {(fields, { add, remove }) => { return ( <> {fields.map((field, index) => ( <Form.Item key={field.key}> <Input name={[field.name, 'inputName']} fieldKey={[field.fieldKey, 'inputKey']} placeholder="Input something" /> <Button onClick={() => remove(field.name)}>Remove</Button> </Form.Item> ))} <Button onClick={() => add()}>Add</Button> </> ); }} </Form.List> ``` 2. 在嵌套的表单项(子表单)中使用 Field 包裹需要收集数据的表单项,例如: ```jsx <Form.List name={['nestedForm', index, 'subNestedForm']}> {(fields, { add, remove }) => { return ( <> {fields.map((field, index) => ( <Form.Item key={field.key}> <Input name={[field.name, 'inputName']} fieldKey={[field.fieldKey, 'inputKey']} placeholder="Input something" /> <Button onClick={() => remove(field.name)}>Remove</Button> </Form.Item> ))} <Button onClick={() => add()}>Add</Button> </> ); }} </Form.List> ``` 在上述代码中,`name` 属性设置为数组形式,使用 `[]` 包裹每级表单项的名称,例如 `'nestedForm'` 表示父表单中的嵌套表单,`'subNestedForm'` 表示子表单中的嵌套表单。使用 `index` 变量表示当前表单项的下标。 通过以上方式,就可以在 Ant Design 中实现表单的嵌套和收集数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值