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

[Bug Report] DatetimePicker 时间选择自动跳动、异常跳动问题 #3232

Closed
moonflame opened this issue Jun 3, 2020 · 9 comments
Closed

Comments

@moonflame
Copy link

设备

小米 10 Android 10

VantWeapp 版本

1.3.1

基础库版本

2.11.1

请提供核心代码片段链接

https://gist.github.com/moonflame/2604229aec51f41a69adf82bda94509d

描述问题

  1. 反复自动跳动:
    连续数次快速连续滑动/点击后,控件会开始自行跳动。
    删除为自己赋值的 input 事件后不会出现该问题。
    Windows 版开发工具模拟器没有该问题。

  2. 月份、日期异常跳动:
    在设置了 min-date 的情况下,在已经选中其他年/月时,选中最小年/月后,月/日会出现跳动;或者在已经选中最小年/月时,选中其他更大的年/月,也会出现跳动。
    设置最小月不是 1 月时,月份会出现跳动问题;最小日不是 1 日时,日期会出现跳动问题。
    不设置 min-date 的情况下不会出现该问题。
    Windows 版开发工具 Stable v1.03.2005140 模拟器也可以复现该问题。

@rex-zsd
Copy link
Collaborator

rex-zsd commented Jun 3, 2020

#3206 可以关注这个

@moonflame
Copy link
Author

#3206 可以关注这个

我看到了那个 issue,他描述不清,代码片段也略复杂。
所以我专门测试了几种情况,写了简单的代码片段和详细的描述,专门开了这个 issue,希望对你们排查问题有所帮助。

@Sramler
Copy link

Sramler commented Jul 21, 2020

我在小米9使用也存在这个问题

@wizzeng
Copy link

wizzeng commented Oct 31, 2020

红米 K20 Pro 安卓10同样出现问题

@NameWjp
Copy link

NameWjp commented Apr 20, 2021

@moonflame 我遇到了相同的问题,研究了下代码说下个人的理解。反复跳动的原因在于 datetime-picker 组件内部会 watch 外部传入的 value 的变化,在值不同的时候会更新组件内部一些状态并异步的通过 $emit('input', val) 通知父组件。

以你提供的例子为例,理想情况是用户选择时间 => 触发 $emit 事件 => value 被重新赋值 => 触发 watch => 传入的值和当前组件内部值比对,结果一致 => 结束。

但现实的情况并不是这样,$emit 的触发是在 this.set 之后,this.set 内部是调用的 wx.nextTick 钩子。也就是说 $emit 会在 wx.nextTick 钩子的回调中执行,是一个异步操作。官方对 wx.nextTick 的描述是 延迟到下一个时间片再执行 ,也就是说回调执行的时机是不确定的,在频繁的触发 input 进行更新操作时并不能保证内外状态一致,代码就会出现一个死循环。

举个例子:当前组件的默认日期的 20号,用户选择了 25号 的日期,又在极快的速度下选择了 27号 的日期,具体分析下组件内部更新流程:

// 初始状态
内:20   外:20
// 由于选择的极快,内部状态已经变为 27,而派发的 $emit 事件还在路上,值分别为 [25, 27]
内:27   外:20   两个更新事件在路上
// 第一个更新事件到达,更新值为 25,此时的更新队列为 [27]
内:27   外:25
// 外部通过赋值触发 watch,内部组件通过重新比对后发现值不相同,触发 input 事件,同时又派发一个 $emit 事件进行更新,更新值为  25,此时的更新队列为 [27, 25]
内:25   外:25
// 又一个更新队列到达,值为 27,此时的更新队列为 [25]
内:25   外:27
// 外部通过赋值触发 watch,内部组件通过重新比对后发现值不相同,触发 input 事件,同时又派发一个 $emit 事件进行更新,更新值为  27,此时的更新队列为 [25, 27]
内:27   外:27
// 又一个更新队列到达,值为 25,此时的更新队列为 [27]
内:27   外:25
// 外部通过赋值触发 watch,内部组件通过重新比对后发现值不相同,触发 input 事件,同时又派发一个 $emit 事件进行更新,更新值为  25,此时的更新队列为 [27, 25]
内:25   外:25

我们可以看到,内外状态会在 25 到 27 之间无限循环。出现这个问题的本质原因在于:我们 watch 到 value 的变化时,只需要通过比对更新组件内部的状态,并不需要 $emit('input', val) 去异步通知父组件去更新,因为这个值本来就是父传递过来的。

解决办法有两种

  1. watch value 变化时不要 $emit('input', val) ,这个需要官方修改,例如:
updateValueNotEmit() {
  const { data } = this;
  const val = this.correctValue(data.value);
  const isEqual = val === data.innerValue;
  if (!isEqual) {
    this.updateColumnValue(val);
  } else {
    this.updateColumns();
  }
},
  1. 不要改变传给 van-datetime-picker 的 value 的值

目前采用的第二种方法,但我发现还有其他的问题,这个异步 emit 更新的值很慢,导致用户选择完时间后,马上点击确定的时候值还没被写入,测试了下要等 1s 到 2s 左右才行,慢的原因可能是内部会计算用户滚动落点,暂时没啥好的解决办法。

@LCBbest
Copy link

LCBbest commented Jul 29, 2021

都快2年了 还没解决吗?

@llccing
Copy link

llccing commented Sep 17, 2021

微信原生 picker 可用

@moonter10-09
Copy link

y有赞什么时候倒闭?

@XieZong
Copy link

XieZong commented Aug 10, 2022

我把 datetime-picker/index.js 文件 onChange 方法中 this.updateColumnValue(value) 改为 this.set({ innerValue: value }) 就没问题了

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

No branches or pull requests

9 participants