Tech Blog - Akihiro Suzuki

【React】子要素のクリックイベントを親要素に伝播させたくない時の実装を考える

January 17, 2023

例えば、以下のようなモーダルを実装したとします。 半透明の背景をクリックした時、モーダルが閉じる単純な実装です。

<Background onClick={e => handleCloseModal(e)}>
  <Content>
    <h2>何かのモーダル</h2>
    <p>ほげほげほげほげほげほげ...</p>
    <Button onClick={e => handleCommit(e)}>決定</Button>
  </Content>
</Background>

しかし、これでは問題があります。モーダルの中身である<Conetnt>...</Conetnt>の部分をクリックした時も<Background />のクリックイベントが呼ばれてモーダルが閉じてしまいます。

子要素のクリックイベントが親要素に伝播されるような動きになりますね。

よくある対処法

適当にググると、以下の対処法が出てきます。

event.stopPropagation()

これを使って、上のモーダルを以下のように改良します。

<Background onClick={e => handleCloseModal(e)}>
  <Content onClick={e => e.stopPropagation()}>
    <h2>何かのモーダル</h2>
    <p>ほげほげほげほげほげほげ...</p>
    <Button onClick={e => handleCommit(e)}>決定</Button>
  </Content>
</Background>

目的通り、モーダルの中身をクリックしてもモーダルは閉じられないようになりました。

勿論これで十分なのですが、、、

クリックイベントを親要素に伝播させないためだけにわざわざ<Content /> のクリックイベントを取るような実装になるのがモヤっとします。(個人の感想です)

クリックイベントが発生した要素をチェックする

e.targete.currentTarget を使うことでチェックできます。

参照 説明
Event.target イベントが発生した要素
Event.currentTarget イベントハンドラが装着された要素

具体的には、 e.targete.currentTarget が一致しているかをチェックします。

const handleCloseModal = useCallback((e: React.MouseEvent) => {
  if (e.target === e.currentTarget) {
    // TODO: クリックイベント処理
  }
}, [])

こうすれば、 <Content>...</Content> のクリックイベントを取らずとも、handleCloseModal 関数だけで完結できます。

<Background onClick={e => handleCloseModal(e)}>
  <Content>
    <h2>何かのモーダル</h2>
    <p>ほげほげほげほげほげほげ...</p>
    <Button onClick={e => handleCommit(e)}>決定</Button>
  </Content>
</Background>

profile

鈴木 章弘 (Suzuki Akihiro)
サーバーサイドだったりフロントエンドだったり時々モバイルアプリ
profile