【react native】TouchableOpacity と Pressable それぞれのネストした時の挙動と、それを組み合わせた時の挙動

# はじめに
今回は、react nativeで`TouchableOpacity`と`Pressable`のネストした時の挙動、およびそれらを組み合わせた時の挙動等をざっと羅列していきます。

## 環境
- react native 0.73.6
- expo 50.0.0
- IOSシミュレータ ios 17.5

## 本題
### TouchableOpacityのネスト
```tsx
<TouchableOpacity onPress={() => console.log('t 1')}>
  <TouchableOpacity onPress={() => console.log('t 2')}>
    <View style={style.rect} />
  </TouchableOpacity>
</TouchableOpacity>
```
結果 : `t 2`

### Pressableのネスト
```tsx
<Pressable onPress={() => console.log('p 1')}>
  <Pressable onPress={() => console.log('p 2')}>
    <View style={style.rect} />
  </Pressable>
</Pressable>
```
結果 : `p 2`

単純なネストの場合は、より前面にあるタッチ可能コンポーネントのonPressが発火するようですね。

### Pressable in TouchableOpacity
```tsx
<TouchableOpacity onPress={() => console.log('t 1')}>
  <Pressable onPress={() => console.log('p 2')}>
    <View style={style.rect} />
  </Pressable>
</TouchableOpacity>
```
結果 : `t 1`

### TouchableOpacity in Pressable
```tsx
<Pressable onPress={() => console.log('p 1')}>
  <TouchableOpacity onPress={() => console.log('t 2')}>
    <View style={style.rect} />
  </TouchableOpacity>
</Pressable>
```
結果 : `p 2 t 2`

これが謎です。`Pressable`の`onPress`が発火した後に、`TouchableOpacity`の`onPress`が発火しています。
伝播とも異なり、親のonPress→子のonPressの順で発火しているのも直感に反します。
それぞれの実装からくる挙動なのだとは思いますが...

### Pressable in Pressable in TouchableOpacity
```tsx
<Pressable onPress={() => console.log('p 1')}>
  <Pressable onPress={() => console.log('p 2')}>
    <TouchableOpacity onPress={() => console.log('t 1')}>
      <View style={style.rect} />
    </TouchableOpacity>
  </Pressable>
</Pressable>
```
結果 : `p 2 t 1`


### TouchableOpacity in Pressable in TouchableOpacity
```tsx
<TouchableOpacity onPress={() => console.log('t 1')}>
  <Pressable onPress={() => console.log('p 1')}>
    <TouchableOpacity onPress={() => console.log('t 2')}>
      <View style={style.rect} />
    </TouchableOpacity>
  </Pressable>
</TouchableOpacity>
```
結果 : `p 1 t 2`

これも謎です。Pressable in TouchableOpacityの挙動とはまた違う挙動を示しています

### position: absoluteを使ったPressable
```tsx
<Pressable
  onPress={() => console.log('p 1')}
  style={{ position: 'relative' }}
>
  <Pressable
    style={{ ...style.rect, position: 'absolute' }}
    onPress={() => console.log('p 2 ')}
  />
  <View style={style.rect} />
  <Pressable
    style={{ ...style.rect, position: 'absolute' }}
    onPress={() => console.log('p 3')}
  />
</Pressable>
```
結果 : `p 3`

### position: absoluteを使ったPressable Index設定あり
```tsx
<Pressable
  onPress={() => console.log('p 1')}
  style={{ position: 'relative' }}
>
  <Pressable
    style={{ ...style.rect, position: 'absolute', zIndex: 2 }}
    onPress={() => console.log('p 2 z 2')}
  />
  <View style={style.rect} />
  <Pressable
    style={{ ...style.rect, position: 'absolute', zIndex: 1 }}
    onPress={() => console.log('p 3 z 1')}
  />
</Pressable>
```
結果 : `p 2 z 2`

### position: absoluteを使った TouchableOpacity in Pressable
```tsx
 <View style={style.rect}>
   <Pressable
     onPress={() => console.log('p 1')}
     style={{ position: 'relative' }}
   >
     <Pressable
       style={{ ...style.rect, position: 'absolute', zIndex: 1 }}
       onPress={() => console.log('p 2 z 1')}
     />
     <TouchableOpacity
       style={{ ...style.rect, position: 'absolute' }}
       onPress={() => console.log('t 1 z 0')}
     />
   </Pressable>
 </View>;
```

結果 : `p 2 z 1`

### position: absoluteを使った TouchableOpacity in Pressable (同Index)
```tsx
 <View style={style.rect}>
   <Pressable
     onPress={() => console.log('p 1')}
     style={{ position: 'relative' }}
   >
     <Pressable
       style={{ ...style.rect, position: 'absolute', zIndex: 1 }}
       onPress={() => console.log('p 2 z 1')}
     />
     <TouchableOpacity
       style={{ ...style.rect, position: 'absolute', zIndex: 1 }}
       onPress={() => console.log('t 1 z 1')}
     />
   </Pressable>
 </View>;
```
結果 : `p 2 z 1`

### position: absoluteを使った TouchableOpacity in Pressable (異なるIndex)
```tsx
 <View style={style.rect}>
   <Pressable
     onPress={() => console.log('p 1')}
     style={{ position: 'relative' }}
   >
     <Pressable
       style={{ ...style.rect, position: 'absolute', zIndex: 1 }}
       onPress={() => console.log('p 2 z 1')}
     />
     <TouchableOpacity
       style={{ ...style.rect, position: 'absolute', zIndex: 2 }}
       onPress={() => console.log('t 1 z 2')}
     />
   </Pressable>
 </View>;
```
結果 : `p 2 z 1`

## まとめ
`TouchableOpacity`と`Pressable`のネストした時の挙動、およびそれらを組み合わせた時の挙動についてざっとまとめてみました。
ほとんどは直感通りの挙動ですが、TouchableOpacity in Pressableのように特殊な挙動を示すケースもあるようです。
`TouchableOpacity`と`Pressable`を組み合わせることで、直感に反する挙動が発生しているように見受けられるので、プロジェクトごとにどちらを使用するか決めておいた方が良いかもしれません。

最後まで読んでいただき、ありがとうございました。