Skip to content

Commit f990e16

Browse files
committed
Fix touchable onPress being dispatched by mistake on scroll on mobile web
https://github.com/necolas/react-native-web/issues/1219\#issuecomment-537724507
1 parent 2000f87 commit f990e16

File tree

2 files changed

+69
-16
lines changed

2 files changed

+69
-16
lines changed

packages/components/src/components/common/Link.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import React, {
66
useLayoutEffect,
77
useRef,
88
} from 'react'
9-
import { StyleSheet, Text, View } from 'react-native'
9+
import { GestureResponderEvent, StyleSheet, Text, View } from 'react-native'
1010

1111
import { useHover } from '../../hooks/use-hover'
1212
import { Browser } from '../../libs/browser'
@@ -185,11 +185,14 @@ export const Link = React.forwardRef<Touchable, LinkProps>((props, ref) => {
185185
default: {
186186
...otherProps,
187187
onPress: href
188-
? (e: any) => {
188+
? (e: GestureResponderEvent) => {
189189
if (onPress) onPress(e)
190+
if (e && e.isDefaultPrevented()) return
190191

191192
if (href.startsWith('http')) Browser.openURL(href)
192-
else Linking.openURL(href)
193+
else if (!href.startsWith('javascript:')) Linking.openURL(href)
194+
195+
if (e) e.preventDefault()
193196
}
194197
: onPress,
195198
} as any,
@@ -200,8 +203,11 @@ export const Link = React.forwardRef<Touchable, LinkProps>((props, ref) => {
200203
onPress: href
201204
? (e: any) => {
202205
if (onPress) onPress(e)
206+
if (e && e.isDefaultPrevented()) return
203207

204208
if (isDeepLink && href) Linking.openURL(href)
209+
210+
if (e) e.preventDefault()
205211
}
206212
: onPress,
207213
selectable: true,

packages/components/src/components/common/Touchable.tsx

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect, useLayoutEffect, useRef } from 'react'
1+
import React, { useCallback, useEffect, useLayoutEffect, useRef } from 'react'
22
import {
33
TouchableHighlightProps,
44
TouchableOpacity,
@@ -34,13 +34,16 @@ export const Touchable = React.forwardRef(
3434
analyticsValue,
3535
onLongPress: _onLongPress,
3636
onPress: _onPress,
37+
onPressIn: _onPressIn,
38+
onPressOut: _onPressOut,
3739
selectable,
3840
tooltip,
3941
...props
4042
}: TouchableProps,
4143
ref,
4244
) => {
4345
const touchableRef = useRef<TouchableOpacity>(null)
46+
const pressInPagePointRef = useRef({ x: 0, y: 0 })
4447

4548
useLayoutEffect(() => {
4649
if (typeof ref === 'function') {
@@ -63,35 +66,79 @@ export const Touchable = React.forwardRef(
6366
if (!tooltip && node.removeAttribute) node.removeAttribute('title')
6467
}, [touchableRef.current, tooltip])
6568

66-
const onPress: typeof _onPress = analyticsLabel
67-
? e => {
69+
const onLongPress: typeof _onLongPress =
70+
_onLongPress ||
71+
(tooltip && Platform.supportsTouch
72+
? () => {
73+
alert(tooltip)
74+
}
75+
: undefined)
76+
77+
const onPressIn = useCallback<NonNullable<TouchableProps['onPressIn']>>(
78+
e => {
79+
if (Platform.OS === 'web') {
80+
pressInPagePointRef.current = {
81+
x: e.nativeEvent.pageX,
82+
y: e.nativeEvent.pageY,
83+
}
84+
}
85+
86+
if (_onPressIn) _onPressIn(e)
87+
},
88+
[_onPressIn],
89+
)
90+
91+
const onPressOut = useCallback<NonNullable<TouchableProps['onPressOut']>>(
92+
e => {
93+
if (_onPressOut) _onPressOut(e)
94+
95+
if (Platform.OS === 'web') {
96+
const [x, y] = [e.nativeEvent.pageX, e.nativeEvent.pageY]
97+
if (
98+
Math.abs(pressInPagePointRef.current.x - x) > 1 ||
99+
Math.abs(pressInPagePointRef.current.y - y) > 1
100+
) {
101+
e.preventDefault()
102+
}
103+
}
104+
},
105+
[_onPressOut],
106+
)
107+
108+
const onPress = useCallback<NonNullable<TouchableProps['onPress']>>(
109+
e => {
110+
if (analyticsLabel) {
68111
analytics.trackEvent(
69112
analyticsCategory || 'button',
70113
analyticsAction || 'press',
71114
analyticsLabel,
72115
analyticsValue,
73116
)
74-
if (_onPress) _onPress(e)
75117
}
76-
: _onPress
77118

78-
const onLongPress: typeof _onLongPress =
79-
_onLongPress ||
80-
(tooltip && Platform.supportsTouch
81-
? () => {
82-
alert(tooltip)
83-
}
84-
: undefined)
119+
if (e && e.isDefaultPrevented()) return
120+
if (_onPress) _onPress(e)
121+
},
122+
[
123+
_onPress,
124+
analyticsCategory || 'button',
125+
analyticsAction || 'press',
126+
analyticsLabel,
127+
analyticsValue,
128+
],
129+
)
85130

86131
return (
87132
<TouchableComponent
88133
{...props}
89134
ref={touchableRef}
90135
data-touchable
91136
data-touchable-disabled={!!props.disabled}
92-
data-touchable-onpress={!!onPress}
137+
data-touchable-onpress={!!_onPress}
93138
onLongPress={onLongPress}
94139
onPress={onPress}
140+
onPressIn={onPressIn}
141+
onPressOut={onPressOut}
95142
style={[
96143
props.disabled && { opacity: 0.5 },
97144
selectable === true && ({ userSelect: undefined } as any),

0 commit comments

Comments
 (0)