How to prevent parent click event in react native?

Viewed 13253

onParentClick = () => {
  console.log('Parent is triggered');
}

onChildClick = (event) => {
  event.stopPropagation();
  console.log('Child is triggered');
}
<TouchableWithoutFeedback onPress={()=> this.onParentClick()}>
  <View>
    <Text>How to prevent parent click event</Text>
    <TouchableOpacity onPress={(event)=> this.onChildClick(event)}>
      <Text> Click Me </Text>
    </TouchableOpacity>
  </View>
</TouchableWithoutFeedback>
<!-- edit description:- there was this end curly brace missing in above,
 however the snippet will not run because the language js will not 
support it and language html will not be able to format it correctly or run it.
(need to run the snippet on the react native environment like code-pen)  -->

Expected: On click of "Click Me", onChildClick() must be called

Issue: On click of "Click Me", onParentClick() is called.

How to prevent parents click event on click of "Click Me"?

Result which I get on click of parent is "Parent is triggered" and it works perfect.

But on click on child the result I get is "Parent is triggered".

I guess onChildClick() is not being triggered.

9 Answers

Old question, but I faced the same issue and in my case, the problem was:

import {TouchableOpacity} from 'react-native';
import {TouchableWithoutFeedback} from 'react-native-gesture-handler';

This weird import happened due to automatic suggestion by eslint. Somehow these libraries don't play nicely together, so I changed the above to:

import {TouchableOpacity, TouchableWithoutFeedback} from 'react-native';

You can simply set the parents pointerEvents property to 'box-none'. By doing so, the parent will not respond to touch events but its child views will get clicked.

  <TouchableWithoutFeedback
      pointerEvents={'box-none'}
  >
      <TouchableWithoutFeedback
          onPress={()=>{
              //onPress here will work
          }}
      />
  </TouchableWithoutFeedback>

You need to "swallow" the click event somewhere. It could be in your outer TouchableWithoutFeedback (ie: change onPress={()=> this.onParentClick()} to onPress={(e) => e.preventDefault()} or you could wrap your TouchableOpacity in another touch handler, like below.

<TouchableWithoutFeedback onPress={()=> this.onParentClick()}>
  <View>
    <Text>How to prevent parent click event</Text>
    <TouchableWithoutFeedback onPress={(e) => e.preventDefault()}
      <TouchableOpacity onPress={(event)=> this.onChildClick(event)>
        <Text> Click Me </Text>
      </TouchableOpacity>
    </TouchableWithoutFeedback>
  </View>
</TouchableWithoutFeedback>

The behavior you described works as expected. This is because TouchableOpacity is a child component of TouchableWithoutFeedback, and so the click event propagates up the tree, triggering the click event of TouchableWithoutFeedback. To solve this issue, there is an event.stopPropagation() method you can call, which will stop the event from bubbling up.

Something like this should work.

onChildClick = (event)=>{ //do your logic here event.stopPropagation(); }

You'd want to use onTouchEnd not press

This would be the simplest answer:

use this on the inner view at the point where you want the propagation to stop

onTouchEnd={(e) => {
   e.stopPropagation()
}}

I had the similar issue, resolved it simply by using zIndex, not only it helped me capture child's onPress but also parent's onPress too, I was able to perform two different operations in a single component

A trigger method:

const childPresses = {}
if(canChildPress) {
  childPresses.onPress = (event) => {this.onChildClick(event)}
}

return <TouchableWithoutFeedback onPress={()=> this.onParentClick()}>
  <View>
    <Text>How to prevent parent click event</Text>
    <TouchableOpacity {...childPresses} >
      <Text> Click Me </Text>
    </TouchableOpacity>
  </View>
</TouchableWithoutFeedback>

Use TouchableOpacity from gesture handler

import { TouchableOpacity } from "react-native-gesture-handler";

then change your style to containerStyle instead

 <TouchableOpacity onPress={props.onPress} containerStyle={styles.container}>

everything should look good now...

Related