Jay Gould

How to detect a tap outside of an element in React Native

May 09, 2019

A common requirement with software UX is to have an element pop over the screen and having the user be able to tap outside of that new component to close it. The world would be a bad place if this UX pattern did not exist:

Popover example

The example above is for a web interface, but it’s just as important for mobile. I found it difficult to achieve this in React Native initially as the task is a little more difficult than the web, but I ended up finding a solution I liked. The solution relies on the onStartShouldSetResponder prop in React Native.

The onStartShouldSetResponder is one of a few props which are available to View and all variations of View (such as ScrollView). This returns an evt value, which contains loads of information about the tap event. Amongst this plethora of information is the target, which is the native element ID of the element which the user has directly tapped.

Our solution is to take this target element ID, and compare it to the element ID of the popover element. Below is an example of how this can work and bare in mind that I’ve taken out all of the messy styling and functionality for making the actual popup, as this example is to show how to simple log if the user has tapped inside or outside of an element:

<View
  onStartShouldSetResponder={(evt) => {
    evt.persist()
    if (this.childrenIds && this.childrenIds.length) {
      if (this.childrenIds.includes(evt.target)) {
        return
      }
      console.log("Tapped outside")
    }
  }}
>
  // popover view - we want the user to be able to tap inside here
  <View
    ref={(component) => {
      this.childrenIds = component._children[0]._children.map(
        (el) => el._nativeTag
      )
    }}
  >
    <View>
      <Text>Option 1</Text>
      <Text>Option 2</Text>
    </View>
  </View>
  // other view - we want the popover to close when this view is tapped
  <View>
    <Text>
      Tapping in this view will trigger the console log, but tapping inside the
      view above will not.
    </Text>
  </View>
</View>

The “popover view” which I’ve referenced in the comment above uses the ref prop to get the element ID which we want to ignore (because we want the popover element to stay open). The ref prop returns all the data about the component, and within this plethora of data is an array of child components. All we do here is map the array of children to return just the ID’s, and save this to the local component class variable.

Notice how I’m not using state, and this is because the component does not n eed to respond to a change in the values.

The child ID’s are used on the tap event to tell us if we’ve tapped one of those elements or not. Simple!


Senior Engineer at Haven

© Jay Gould 2023, Built with love and tequila.