Taro: 子组件的标签有id属性时,父组件的回调函数异常

Created on 12 Aug 2020  ·  4Comments  ·  Source: NervJS/taro


相关平台

微信小程序

复现仓库

https://github.com/li-z/taro-bug
小程序基础库: 2.12.0
使用框架: React

复现步骤

运行项目后点击跳转到详情页,然后返回首页。然后触发子组件的 onClick 事件时,回调函数却还是详情页的函数。去掉id属性后正常。

期望结果

有无id属性都应正常

实际结果

有id属性异常

环境信息

Taro CLI 3.0.7 environment info:
    System:
      OS: macOS 10.14.1
      Shell: 3.2.57 - /bin/bash
    Binaries:
      Node: 10.16.2 - ~/.nvm/versions/node/v10.16.2/bin/node
      Yarn: 1.17.3 - ~/.nvm/versions/node/v10.16.2/bin/yarn
      npm: 6.9.0 - ~/.nvm/versions/node/v10.16.2/bin/npm
    npmPackages:
      @tarojs/components: 3.0.7 => 3.0.7 
      @tarojs/mini-runner: 3.0.7 => 3.0.7 
      @tarojs/react: 3.0.7 => 3.0.7 
      @tarojs/runtime: 3.0.7 => 3.0.7 
      @tarojs/taro: 3.0.7 => 3.0.7 
      @tarojs/webpack-runner: 3.0.7 => 3.0.7 
      babel-preset-taro: 3.0.7 => 3.0.7 
      eslint-config-taro: 3.0.7 => 3.0.7 
      react: ^16.10.0 => 16.13.1 
    npmGlobalPackages:
      typescript: 3.5.3

F-react T-weapp V-3 good first issue

Most helpful comment

你的子组件ID一样的话,会有这个问题,taro自己会生成一个UID,一个UID对应一个hash里面的一个事件

ID不一样

哈哈哈哈😂,和我的问题一样,搞得我都开始怀疑react了。
我遇到了同样的问题,但是我发现在小程序中会出现这种现象,但是编译成h5没这个问题,经过一步步调试,发现是因为id的问题。taro编译成微信小程序(我只开发了微信小程序,其它平台小程序代码没看)时,会给每一个组件生成一个唯一id,然后使用id来派发事件(并不是将事件绑定到元素上),这个有点像:document.getElementById(id).onclick=function(){},因此如果你的id相同(在整个程序中)后面的事件函数就会把前面的事件函数覆盖掉。

你说你的Child.tsx中的ID的不同,只看Child.tsx文件两个View的ID确实是不同的,但是你可能忘记了你将Child.tsx分别在index.tsx和detail.tsx中使用了且Child.tsx中的ID是写死的,这样就会导致index.tsx和detail.tsx中的<View id='a1' onClick={onClick}>a1</View>具有相同的ID都是a1,当打开detail.tsx页面时,a1的点击事件函数就会被替换掉,a2同样如此。

解决方案就是保证每次调用组件生成的id是不同的,需要动态生成id不能写死。我的解决方案是使用固定前缀+时间戳。如果你不需要这个id你可以不写,这样taro就会自动生成一个唯一的id

下面是调试时看到Taro位于src/dom/event.ts的源代码:

export function eventHandler (event: MpEvent) {
  if (event.currentTarget == null) {
    event.currentTarget = event.target
  }

  const node = document.getElementById(event.currentTarget.id)
  if (node != null) {
    node.dispatchEvent(createEvent(event, node))
  }
}

我认为Taro应该在官方文档中注明必须保持id是全局唯一的。

All 4 comments

你的子组件ID一样的话,会有这个问题,taro自己会生成一个UID,一个UID对应一个hash里面的一个事件

你的子组件ID一样的话,会有这个问题,taro自己会生成一个UID,一个UID对应一个hash里面的一个事件

ID不一样

你的子组件ID一样的话,会有这个问题,taro自己会生成一个UID,一个UID对应一个hash里面的一个事件

ID不一样

哈哈哈哈😂,和我的问题一样,搞得我都开始怀疑react了。
我遇到了同样的问题,但是我发现在小程序中会出现这种现象,但是编译成h5没这个问题,经过一步步调试,发现是因为id的问题。taro编译成微信小程序(我只开发了微信小程序,其它平台小程序代码没看)时,会给每一个组件生成一个唯一id,然后使用id来派发事件(并不是将事件绑定到元素上),这个有点像:document.getElementById(id).onclick=function(){},因此如果你的id相同(在整个程序中)后面的事件函数就会把前面的事件函数覆盖掉。

你说你的Child.tsx中的ID的不同,只看Child.tsx文件两个View的ID确实是不同的,但是你可能忘记了你将Child.tsx分别在index.tsx和detail.tsx中使用了且Child.tsx中的ID是写死的,这样就会导致index.tsx和detail.tsx中的<View id='a1' onClick={onClick}>a1</View>具有相同的ID都是a1,当打开detail.tsx页面时,a1的点击事件函数就会被替换掉,a2同样如此。

解决方案就是保证每次调用组件生成的id是不同的,需要动态生成id不能写死。我的解决方案是使用固定前缀+时间戳。如果你不需要这个id你可以不写,这样taro就会自动生成一个唯一的id

下面是调试时看到Taro位于src/dom/event.ts的源代码:

export function eventHandler (event: MpEvent) {
  if (event.currentTarget == null) {
    event.currentTarget = event.target
  }

  const node = document.getElementById(event.currentTarget.id)
  if (node != null) {
    node.dispatchEvent(createEvent(event, node))
  }
}

我认为Taro应该在官方文档中注明必须保持id是全局唯一的。

不太好改,所有页面都挂在同一颗 DOM 树下,共用一个 eventSource,暂时先保证组件全局 id 唯一吧。

我们先在文档加个提示。

Was this page helpful?
0 / 5 - 0 ratings