Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

微信小程序中,在增加数组元素时,组件属性中无法正确的更新数组的 length #882

Closed
hitomi opened this issue Oct 18, 2018 · 13 comments
Assignees

Comments

@hitomi
Copy link

hitomi commented Oct 18, 2018

问题描述
微信小程序中,在增加数组元素时,组件属性中无法正确的更新数组的 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 应当可以正常更新

报错信息

系统信息

  • 操作系统: Windows 10
  • Taro 版本 1.0.7
  • Node.js 版本 v10.6.0
  • 报错平台 weapp

补充信息
通过调查,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>
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 的方法解决问题

// 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
}
@Chen-jj Chen-jj self-assigned this Oct 19, 2018
@luckyadam luckyadam added the enhancement New feature or request label Oct 19, 2018
@Chen-jj
Copy link
Contributor

Chen-jj commented Oct 23, 2018

的确有这样的问题,感谢指出,已修复。

@joyran
Copy link

joyran commented Nov 14, 2018

@hitomi @Chen-jj 这样改后有个新的问题,当数组更新时,比如fromItem = [1,2,3,4], toItem=[1,2,3,4,5],此时setState会更新整个数组toItem,导致数组过大时性能下降

@Chen-jj Chen-jj reopened this Nov 16, 2018
@Chen-jj
Copy link
Contributor

Chen-jj commented Nov 19, 2018

@joyran 我们讨论过你的观点后,决定还是要先保证程序的正确性。这个 issue 会先开着,我们会尝试添加一个额外的 API 给开发者绕过 diff 按路径更新。

@joyran
Copy link

joyran commented Nov 20, 2018

当数组很大时性能下降的太厉害了,希望早日解决,也非常感谢你们开发的这个amazing框架。

@Chen-jj
Copy link
Contributor

Chen-jj commented Nov 20, 2018

@joyran
Copy link

joyran commented Nov 21, 2018

@Chen-jj 我已经提供了代码片段给微信官方了,好奇怪当点击第一个按钮时, {{contents.length}} 中的内容 contents.length 已经变为1了,但是三元运算中 contents.length 还是 0,所以会出现按钮不可点击,但是文本已经变成1了。

@joyran
Copy link

joyran commented Dec 17, 2018

@Chen-jj hello,这个问题现在微信官方好像没有修复的计划,那Taro准备怎么处理呢

@joyran
Copy link

joyran commented Dec 24, 2018

@Chen-jj hello,有没有计划提供一个单独的接口可以路径更新的方式增加数组元素

@Chen-jj
Copy link
Contributor

Chen-jj commented Dec 24, 2018

@joyran 还在考虑中。

@joyran
Copy link

joyran commented Feb 18, 2019

@Chen-jj Hi,这个考虑的怎么样了

@Chen-jj Chen-jj removed the enhancement New feature or request label Mar 5, 2019
@Chen-jj
Copy link
Contributor

Chen-jj commented Mar 5, 2019

@joyran 为了保持各端一致,单独开接口给用户来解决微信侧独有问题并不合适。最终我们还是决定改回去,微信小程序 diff 数组时,新数组长度大于等于旧数组的长度时都会逐项 diff,按路径更新:2f82f0c

@hitomi 在最新版本(1.2.16)我们即将改回去,你的问题将会复现。最佳做法是单独使用一个 state 值来记录数组长度,而不是直接使用 this.state.contents.length

@hitomi
Copy link
Author

hitomi commented Mar 5, 2019

@Chen-jj 可以考虑在更新时也修改一下文档里面的涉及到这部分问题的示例代码 :)
https://nervjs.github.io/taro/docs/components/base/text.html

@Chen-jj
Copy link
Contributor

Chen-jj commented Mar 5, 2019

@hitomi done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants