ReactでModal windowを実装する
汎用コンポーネントも存在しますが、自作のミニマル実装コードです。
ひとまずコード(スタイルなどは簡略化しています)
import React from 'react'; import PropTypes from 'prop-types'; class ModalWindow extends React.Component { constructor(props) { super(props); this.eventListener = this._handleEscKey.bind(this); this.state = {}; } componentDidMount() { document.body.classList.add('openModal'); document.addEventListener('keydown', this.eventListener); } componentWillUnmount() { document.body.classList.remove('openModal'); document.removeEventListener('keydown', this.eventListener); } _handleEscKey(e) { const keyCode = parseInt(e.keyCode, 10); if (keyCode !== 27) { return false; } this.props.onClose(); } render() { const { children, onClose } = this.props; return ( <div className="modalWin_wrapper" onClick={(e) => e.target === e.currentTarget ? onClose() : null}> <div className="modalWin_inner"> <button className="modalWin_close" onClick={() => onClose()}> × </button> {children} </div> </div> ); } } ModalWindow.propTypes = { children: PropTypes.node.isRequired, onClose: PropTypes.func.isRequired } export default ModalWindow;
.modalWin_wrapper { display: block; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, .5); z-index: 10000; } .modalWin_inner { position: relative; width: 1000px; height: 100%; margin: 0 auto; padding: 0 0 20px; background: #FFF; overflow: scroll; box-sizing: border-box; box-shadow: 0 0 10px 0 rgba(0, 0, 0, .5); } .modalWin_close { display: block; position: absolute; width: 40px; height: 40px; top: 0; right: 0; border: 0; background: #666; font-size: 40px; line-height: 1; color: #FFF; text-align: center; outline: none; } body.openModal { overflow: hidden; }
使うときは、
<ModalWindow onClose={this.onClose.bind(this)}> <div>Content</div> </ModalWindow>
のようにする。
最低限、
- モーダルウィンドウの外側(ここではmodalWin_wrapperクラスのdiv要素)をクリックしたら閉じる
- Escキーを押したら閉じる
- モーダルウィンドウ内にも閉じるボタン(ここでは.modalWin_closeクラスのbutton要素)
- 閉じる時にActionの発行をしたいので、閉じるための関数は外部から渡す
を実装しています。
Escキーが押されたことを検知するにはイベントリスナーの登録が必要なので、componentDidMount()
内でdocument.addEventListener('keydown', this.eventListener);
しています。そしてcomponentWillUnmount()
で削除します。削除する時に同じ関数を引数にする必要があるので、関数はコンストラクターで定義してしまっています。
もう一点、モーダルウィンドウを開いている状態でスクロールをすると、後ろに隠れているコンテンツもスクロールしてしまいます。これではあまり使い勝手がよろしくないので、モーダルウィンドウが開いている間はbody要素にoverflow: hidden
を設定しています。
body要素はReactのマウントポイントの外側にあるため、直接DOMツリーからアクセスしています。この部分:document.body.classList.add('openModal');
。