问题描述
微信小程序中,在增加数组元素时,组件属性中无法正确的更新数组的 length,不过如果作为内容渲染是没问题的,若从数组中删除元素也不会遇到问题
复现步骤
在微信小程序中无论怎么按 add line 按钮,remove line 按钮的状态都是 disabled 的
// 来自官网的范例 :(
// https://nervjs.github.io/taro/docs/components/base/text.html
import Taro, { Component } from '@tarojs/taro'
import { View, Text } from '@tarojs/components'
export default class PageView extends Component {
constructor () {
super(...arguments)
this.state = {
contents = []
}
}
add = e => {
const cot = this.state.contents
cot.push({text: 'hello world'})
this.setState(() => {
return {contents: cot}
})
}
remove = e => {
const cot = this.state.contents
cot.pop()
this.setState(() => {
return {contents: cot}
})
}
render () {
return (
<View className='container'>
{this.state.contents.map(item => {
return (
<Text>{item.text}</Text>
)
})}
<Button className='btn-max-w button_style' plain type='default' onClick={this.add}>add line</Button>
<Button className='btn-max-w button_style' plain type='default' disabled={this.state.contents.length ? false:true} onClick={this.remove}>remove line</Button>
</View>
)
}
}
期望行为
在增加数组元素时,组件属性中的数组的 length 应当可以正常更新
报错信息
无
系统信息
补充信息
通过调查,taro 目前更新数组的方式是先 diff,若数组长度减小则会直接替换掉整个数组,若数组的长度没有减小,会通过 数据路径 的方式去 setData,类似于
this.setData({
"content[1]": { text: 233 }
})
进一步调查发现,这可能是微信小程序的一个BUG,通过 数据路径 的方式增加数组元素可能导致组件属性中无法正确获取数组的 length,但直接替换掉整个数组是没问题的,而且只在作为组件属性的时候会出问题,作为文本渲染则不会受到影响
原生的微信小程序中的复现方法
<!-- index.wxml -->
<block>
<view class="container">
<text wx:for="{{contents}}" wx:for-item="item">{{item.text}}</text>
<button bindtap="add">add line by path</button>
<button bindtap="add2">add line by replace array</button>
<button bindtap="log" data-content="{{contents}}" data-content-length="{{contents.length}}">show log</button>
<button disabled="{{contents.length ? false : true}}">{{contents.length}}</button>
</view>
</block>
```js
let i = 0
Page({
data: {
contents: [],
},
add () {
this.setData({
[contents[${i++}]]: { text: 'test' }
})
},
add2() {
this.setData({
contents: [...this.data.contents, { text: 'test' }]
})
},
log (ev) {
console.log({
contentLength: ev.currentTarget.dataset.contentLength,
content: ev.currentTarget.dataset.content
})
}
})
猜测可以暂时通过调整一下 diff 的方法解决问题
```js
// https://github.com/NervJS/taro/blob/4ed1d45ff883fb12b807a3c69e6b588393ff5184/packages/taro-weapp/src/util.js#L114
// https://github.com/NervJS/taro/blob/4ed1d45ff883fb12b807a3c69e6b588393ff5184/packages/taro-weapp/src/util.js#L161
if (toItem.length < fromItem.length) {
res[targetKey] = toItem
} else {
// 数组
diffArrToPath(toItem, fromItem, res, `${targetKey}`)
}
修改为只有数组元素数量一致的时候再去 diff
if (toItem.length === fromItem.length) {
// 数组
diffArrToPath(toItem, fromItem, res, `${targetKey}`)
} else {
res[targetKey] = toItem
}
的确有这样的问题,感谢指出,已修复。
@hitomi @Chen-jj 这样改后有个新的问题,当数组更新时,比如fromItem = [1,2,3,4], toItem=[1,2,3,4,5],此时setState会更新整个数组toItem,导致数组过大时性能下降
@joyran 我们讨论过你的观点后,决定还是要先保证程序的正确性。这个 issue 会先开着,我们会尝试添加一个额外的 API 给开发者绕过 diff 按路径更新。
当数组很大时性能下降的太厉害了,希望早日解决,也非常感谢你们开发的这个amazing框架。
@Chen-jj 我已经提供了代码片段给微信官方了,好奇怪当点击第一个按钮时, 中的内容 contents.length 已经变为1了,但是三元运算中 contents.length 还是 0,所以会出现按钮不可点击,但是文本已经变成1了。
@Chen-jj hello,这个问题现在微信官方好像没有修复的计划,那Taro准备怎么处理呢
@Chen-jj hello,有没有计划提供一个单独的接口可以路径更新的方式增加数组元素
@joyran 还在考虑中。
@Chen-jj Hi,这个考虑的怎么样了
@joyran 为了保持各端一致,单独开接口给用户来解决微信侧独有问题并不合适。最终我们还是决定改回去,微信小程序 diff 数组时,新数组长度大于等于旧数组的长度时都会逐项 diff,按路径更新:2f82f0c
@hitomi 在最新版本(1.2.16)我们即将改回去,你的问题将会复现。最佳做法是单独使用一个 state 值来记录数组长度,而不是直接使用 this.state.contents.length
@Chen-jj 可以考虑在更新时也修改一下文档里面的涉及到这部分问题的示例代码 :)
https://nervjs.github.io/taro/docs/components/base/text.html
@hitomi done
Most helpful comment
@Chen-jj 我已经提供了代码片段给微信官方了,好奇怪当点击第一个按钮时, 中的内容 contents.length 已经变为1了,但是三元运算中 contents.length 还是 0,所以会出现按钮不可点击,但是文本已经变成1了。