RN 中 Touchable*
组件内部元素的触摸事件会被 Touchable*
组件捕获。这个是符合预期的。但是如果 Touchable*
组件内部嵌套一个 ScrollView
组件,会导致 ScrollView
组件滚动异常(卡顿)。
解决
这个问题最初是同事在 rnx-ui/Overlay 组件的使用过程中发现的。下面就结合 Overlay
组件介绍下该问题的 3 个解法。
最初 Overlay
组件结构如下:
<TouchableWithoutFeedback>
<View>
{this.props.children}
</View>
</TouchableWithoutFeedback>
实际使用产生结构如下:
<TouchableWithoutFeedback>
<View>
<ScrollView>
{list}
</ScrollView>
</View>
</TouchableWithoutFeedback>
解法 1:禁止外层捕获事件
只要 Overlay
不捕获自组件的触摸事件就好了。View
组件有个属性叫做 pointerEvents
,用来控制 View
是否可以作为触控事件的目标。(具体参考:https://facebook.github.io/re…)
可以通过如下写法让 Touchable*
组件内部元素的触摸事件无法被外层捕获:
<TouchableWithoutFeedback>
<View pointerEvents="box-none">
<ScrollView>
{list}
</ScrollView>
</View>
</TouchableWithoutFeedback>
设置 pointerEvents
的值为 box-none
,让 View
无法接受触摸事件,但是它的子元素可以。这样就避免了 ScrollView
内部触摸事件被外层捕获导致卡顿。
缺点: 作为 Overlay
组件的话,就无法实现“点击遮罩消失”这类功能了。
解法 2:阻止 ScrollView 内部事件冒泡到外层
阻止 ScrollView
组件的事件冒泡到外层也可以解决问题。ScrollView
内部用 Touchable*
组件包裹即可:
<TouchableWithoutFeedback>
<View>
<ScrollView>
<TouchableWithoutFeedback>
{list}
</TouchableWithoutFeedback>
</ScrollView>
</View>
</TouchableWithoutFeedback>
缺点: 需要业务特别处理
解法 3:改变层级关系
从根本解决问题,将处理触摸事件的层和子组件层的层级关系由父子关系改为兄弟关系:
<View>
<TouchableWithoutFeedback>
<View />
</TouchableWithoutFeedback>
{this.props.children}
</View>
缺点: 无。本该如此。
总结
归根结底,还是一个设计问题。