Published on

Reactでコンポーネントの外側がクリックされか判別する

Authors

モーダルやダイアログの UI の場合、モーダルの外側がクリックされたら閉じるような UI って結構見ますよね。
ただそれ実際に React で作るのどうやるんだっけ?と思ったので今回は実際に作ってみることにしました。

結論

コードだけ欲しいという方もいると思うので最終的なコードを最初に置いておきます。 https://codepen.io/ichiki1023/pen/ExaZPJq

class Modal extends React.Component {
  constructor(props) {
    super(props)

    // Refの定義
    this.modalRef = React.createRef()
    this.handleClickEvent = this.handleClickEvent.bind(this)
  }

  componentDidMount() {
    // EventTargetに全てのClick eventをHandlingできるように登録する
    document.addEventListener('click', this.handleClickEvent)
  }

  componentWillUnmount() {
    // click eventがeventListenerに登録されたままになってしまうのでUnmount時にremoveする
    document.removeEventListener('click', this.handleClickEvent)
  }

  handleClickEvent(event) {
    if (this.modalRef && this.modalRef.current && !this.modalRef.current.contains(event.target)) {
      // ref内にクリックされたeventのDOMが含まれているかを確認する
      console.log('Clicked outside!')
    }
  }

  render() {
    return (
      <div ref={this.modalRef} className={'modal'}>
        Modal
      </div>
    )
  }
}

解説

今回の実装において必要なこと

  1. Ref の定義
  2. click event を EventTarget に登録
  3. click された event を見て DOM ノードに含まれているかチェック

ref の定義

Ref は render メソッドで作成された DOM ノードもしくは React の要素にアクセスする方法を提供します

【公式】Ref と DOMより

つまり Ref を定義すると現在のコンポーネントの DOM ノードにアクセスできるようになります。
出力するとこんな感じ

console.log(this.modalRef.current) // DOMのnodeを参照するためにcurrent propertyを見ます


↓ Result


"<div class='modal'>Modal</div>"

2. click event を EventTarget に登録

次に現在のコンポーネントで全ての click event を handling できるようにします。 ただの onClick を定義したとしてもコンポーネント内のクリックしか判定できないので EventTarget で画面内の全ての click event を handling できるように登録します。

componentDidMount() {
   // EventTarget に全ての Click event を Handling できるように登録する
   document.addEventListener('click', this.handleClickEvent)
}

これでどこをクリックしても event を取れるようになりました。

3. click された event を見て Ref に含まれているかチェック

Ref の定義と、全ての Click Event を Handling できるようになったら後は組み合わせるだけです。
ref.current 内に event.target の内容が含まれていない場合はコンポーネントの外側がクリックされたことになるためあとは条件分岐を作ってあげれば完成です。

handleClickEvent (event) {
   if (
      this.modalRef &&
      this.modalRef.current &&
      !this.modalRef.current.contains(event.target)
   ) {
      // ref 内にクリックされた event の DOM が含まれているかを確認する
      console.log('Clicked outside!')
   }
}

まとめ

実際に例を見てみると、案外簡単に実装できたのではないでしょうか。
痒いところに手が届く記事を今後も増やしていければと思います!