dgellow
dgellow

Reputation: 832

Nested Touchable to handle onPress while parent handles onLongPress

I have a simple screen with components like this:

<Parent onLongPress={/* do something */}>
 <Child onPress={/* do something */} />
 <Child onPress={/* do something */} />
<Parent>

I would like any long press on anything within <Parent> to trigger Parent.onLongPress, and short press of children to trigger the correspondant Child.onPress. I learned from the documentation that the parent view can use View.props.onStartShouldSetResponderCapture={(event) => true} to be the one handling the event, but I cannot find a way to do this only for long press events and not all touch events. I initially expected I would be able to use the event type in onStartShouldSetResponderCapture but it seems to always be set to undefined.

I created a Snack if someone wants to try out: https://snack.expo.io/@dgellow/arrogant-strawberries.

Upvotes: 2

Views: 2293

Answers (2)

chinloong
chinloong

Reputation: 3653

Use LongPressGestureHandler from react-native-gesture-handler to capture long press event on the parent component.

Before

        <Button // custom component
          style={styles.container}
          onLongPress={() => {
            // long press handler
          }}
        >
          {children} // with `onPress`
        </Button>
      );

After

import {
  LongPressGestureHandler,
  State,
} from 'react-native-gesture-handler';

...
...

        <LongPressGestureHandler
          onHandlerStateChange={({ nativeEvent }) => {
            if (nativeEvent.state === State.ACTIVE) {
              // long press handler
            }
          }}
        >
          <View style={styles.container}>
            {children} // with `onPress`
          </View>
        </LongPressGestureHandler>

Please note that the direct descendant of LongPressGestureHandler should be a View.

I almost go with context approach due to multiple deeply nested children that needed to call the onLongPress function passed around as props.

If you are using react-navigation, you are using react-native-gesture-handler already.

Upvotes: 4

fredrivett
fredrivett

Reputation: 6584

I had the same problem here, ideally the child would "release" its hold on the capture of a press event after onPress passes and let the parent's onLongPress take over, but I've not found a way to do that.

The solution that worked in my instance was to add the same method for onLongPress to the parent and the child, so if the child takes over, it uses the method passed to it, and if not then the parent calls the method.

Note I've got this working in my codebase, but not tried the specific example below:

const onLongPress = () => alert("long press!");
const onPress = () => alert("short press!");

return (
  <Parent onLongPress={onLongPress}>
   <Child onLongPress={onLongPress} onPress={onPress} />
   <Child onLongPress={onLongPress} onPress={onPress} />
  <Parent>
);

If the child component you want to add the onLongPress & onPress to is further down the Native DOM tree, then you can just keep passing that function down (though it does feel a bit messy if it's many levels nested deep, but I've found no alternative).

Upvotes: 2

Related Questions