Commit a6304b4
authored
Fix: onPress callbacks are invoked for all nested Pressables (#3295)
## Description
<!--
Description and motivation for this PR.
Include 'Fixes #<number>' if this is fixing some issue.
-->
Fixes #3282
The [onStart](https://github.com/software-mansion/react-native-gesture-handler/blob/main/src/components/Pressable/Pressable.tsx#L309-L344) and [onBegin](https://github.com/software-mansion/react-native-gesture-handler/blob/main/src/components/Pressable/Pressable.tsx#L303-L308) callbacks are invoked on the deepest responder on iOS and Android respectively. Inside those methods the `isTouchPropagationAllowed` ref is set to true. Base on that we can assume which `Pressable` should be the responder of the touch on the JS side. Yet there is nothing that would stop invoking `onPress` callbacks if the `isTouchPropagationAllowed` is set to false in [pressOutHandler](https://github.com/software-mansion/react-native-gesture-handler/blob/main/src/components/Pressable/Pressable.tsx#L136-L176). The fix introduces early return in the `pressOutHandler` if clicked `Pressable` is not the deepest one.
On the old architecture the [measure](https://github.com/software-mansion/react-native-gesture-handler/blob/main/src/components/Pressable/Pressable.tsx#L214-L255) method is called asynchronously, usually after the `onStart`. In nested pressables, if the deepest one is clicked it still calls `onTouchesDown` and `onTouchesUp`, which sets [shouldPreventNativeEffects](https://github.com/software-mansion/react-native-gesture-handler/blob/main/src/components/Pressable/Pressable.tsx#L265-L267) to `true`, omitting the `onStart`, which sets it back to `false`. Because of that, when now we tried to click on the outer Pressable it would be stopped in [onStart shouldPreventNativeEvents check](https://github.com/software-mansion/react-native-gesture-handler/blob/main/src/components/Pressable/Pressable.tsx#L338-L341) without setting the `isTouchPropagationAllowed` to `true` and this is the exact same state that led to setting the `shouldPreventNativeEffects` to `true` so it is a cycle. The simplest solution for this is to check if the `onStart` is called before the `measure` in `shouldPreventNativeEffects` check and early return if it's not.
## Test plan
Click on the red box and notice that callbacks on its parent are not invoked. Click on the text input to see that the callbacks on its Pressable parent are also not invoked.
<!--
Describe how did you test this change here.
-->
<details>
<summary>Code</summary>
```ts
import * as React from 'react';
import {
View,
Text,
Pressable as RNPressable,
SafeAreaView,
StyleSheet,
TextInput as RNTextInput,
} from 'react-native';
import {
GestureHandlerRootView,
Pressable as RNGHPressable,
TextInput as RNGHTextInput,
} from 'react-native-gesture-handler';
export default function HomeScreen() {
return (
<GestureHandlerRootView>
<SafeAreaView style={{ flex: 1 }}>
<View style={styles.container}>
<View style={styles.wrapper}>
<RNGHPressable
style={[styles.pressableStyles, styles.outerPressableStyles]}
onPress={() => console.log(JSON.stringify('RNGH: Parent Press'))}
onPressIn={() => console.log('RNGH: Parent PressIn')}
onPressOut={() => console.log('RNGH: Parent PressOut')}>
<RNGHPressable
style={[styles.pressableStyles, styles.innerPressableStyles]}
onPress={() => console.log(JSON.stringify('RNGH: Child Press'))}
onPressIn={() => console.log('RNGH: Child PressIn')}
onPressOut={() => console.log('RNGH: Child PressOut')}>
<Text>RNGH</Text>
</RNGHPressable>
</RNGHPressable>
<RNPressable
style={[styles.pressableStyles, styles.outerPressableStyles]}
onPress={() => console.log(JSON.stringify('RN: Parent Press'))}
onPressIn={() => console.log('RN: Parent PressIn')}
onPressOut={() => console.log('RN: Parent PressOut')}>
<RNPressable
style={[styles.pressableStyles, styles.innerPressableStyles]}
onPress={() => console.log(JSON.stringify('RN: Child Press'))}
onPressIn={() => console.log('RN: Child PressIn')}
onPressOut={() => console.log('RN: Child PressOut')}>
<Text>RN</Text>
</RNPressable>
</RNPressable>
</View>
<View style={styles.wrapper}>
<RNGHPressable
style={styles.textInputPressable}
onPress={() =>
console.log(JSON.stringify('RNGH: TextInput Press'))
}
onPressIn={() => console.log('RNGH: TextInput PressIn')}
onPressOut={() => console.log('RNGH: TextInput PressOut')}>
<RNGHTextInput
placeholder="RNGH TextInput"
style={styles.textInputStyles}
onChangeText={(text) => console.log(text)}
/>
</RNGHPressable>
<RNPressable
style={styles.textInputPressable}
onPress={() => console.log(JSON.stringify('RN: TextInput Press'))}
onPressIn={() => console.log('RN: TextInput PressIn')}
onPressOut={() => console.log('RN: TextInput PressOut')}>
<RNTextInput
placeholder="RN TextInput"
style={styles.textInputStyles}
onChangeText={(text) => console.log(text)}
/>
</RNPressable>
</View>
</View>
</SafeAreaView>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
wrapper: {
alignItems: 'center',
justifyContent: 'center',
gap: 10,
flexDirection: 'row',
marginTop: 10,
},
pressableStyles: {
paddingVertical: 15,
paddingHorizontal: 20,
alignItems: 'center',
justifyContent: 'center',
},
innerPressableStyles: {
backgroundColor: 'red',
width: 120,
height: 50,
},
outerPressableStyles: {
backgroundColor: '#3BE7BB',
},
textInputPressable: {
backgroundColor: 'gray',
padding: 10,
borderRadius: 5,
width: 160,
// height: 50,
alignItems: 'center',
justifyContent: 'center',
},
textInputStyles: {
backgroundColor: 'lightgray',
paddingHorizontal: 20,
paddingVertical: 10,
height: 40
},
pressableText: {
color: '#000',
fontSize: 16,
fontWeight: 'bold',
},
});
```
</details>1 parent f12d9f6 commit a6304b4
File tree
2 files changed
+43
-4
lines changed- example/src/release_tests/nestedPressables
- src/components/Pressable
2 files changed
+43
-4
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
53 | | - | |
54 | | - | |
55 | | - | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
56 | 75 | | |
57 | 76 | | |
58 | 77 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
141 | 159 | | |
142 | 160 | | |
143 | 161 | | |
| |||
340 | 358 | | |
341 | 359 | | |
342 | 360 | | |
343 | | - | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
344 | 364 | | |
345 | 365 | | |
346 | 366 | | |
| |||
0 commit comments