React入門その⑥ react-modal
概要
Webで alert による次のようなそっけないモーダルダイアログボックスでなくて、もう少し見栄えのよいものにしたいような場合、自分で作らなくても既に用意されているものがあるのでそれを使おうという話です。

これを次のようにしてみました。このメッセージ以外の部分の見栄えの変更の中でやっております。

結論としては、随分手間がかかりました。
なお、モーダルダイアログボックスだけでなく、いろいろなものが利用可能(例えば bit.dev 参照)です。
インストール
- 以下はあらかじめインストールされていました。
- react // バージョン 16.12.0
- react-dom // バージョン 16.12.0
- babel-cli // バージョン 6.24.1
- babel-core // バージョン 6.25.0
- babel-loader // バージョン 7.1.1
- babel-plugin-transform-class-properties // バージョン 6.24.1
- babel-preset-env // バージョン 1.7.0
- babel-preset-react // バージョン 6.24.1
- webpack // バージョン 4.41.5
- webpack-cli // バージョン 3.3.10
- webpack-dev-server // バージョン 3.10.1
- 以下をインストールします。
- react-modal // バージョン 3.11.1
- style-loader // バージョン 1.1.2
- css-loader // バージョン 3.4.1
- sass-loader // バージョン 8.0.0、背後でnode-sassを使用する
- node-sass // バージョン 4.13.0
- normalize.css // バージョン 8.0.1
> yarn add react-modal style-loader css-loader
変更前コード
:
:
export default class IndecisionApp extends React.Component {
state = {
options: []
};
:
:
handlePick = () => {
const randomNum = Math.floor(Math.random() * this.state.options.length);
const option = `選ばれた選択肢は ${this.state.options[randomNum]}`;
alert(option);
}
:
:
変更後コード
上記コードに対し、alert を記述した1行が変更され、OptionModal のインポートと state を追加しました。
:
:
import OptionModal from './OptionModal'; // モーダルダイアログボックス
export default class IndecisionApp extends React.Component {
state = {
options: [],
selectedOption: null// 決定された選択肢
};
:
:
handlePick = () => {
const randomNum = Math.floor(Math.random() * this.state.options.length);
const option = this.state.options[randomNum];
this.setState(() => ({ selectedOption: option })); // alertの代わりにここを変更
}
:
:
あと、 OptionModal を描画するために本クラスの render() に以下を追加しました。
<OptionModal
selectedOption={this.state.selectedOption}
handleClearSelectedOption={this.handleClearSelectedOption}
/>
ここでは[了解]ボタンをクリックしたときにモーダルダイアログボックスを閉じるための処理をおこなうため、新たに追加した handleClearSelectedOption を渡しています。これの処理は以下です。
handleClearSelectedOption = () => {
this.setState(() => ({ selectedOption: null}));
}
次のように記載するより簡略された記法となっています。
handleClearSelectedOption = () => {
this.setState(() => {
return ({
selectedOption: null
});
});
}
追加したOptionModal.js は基本は次です。
import React from 'react';
import Modal from 'react-modal';
Modal.setAppElement("#app"); // これがないと警告が出る
const OptionModal = (props) => (
<Modal
isOpen={!!props.selectedOption}
contentLabel="選ばれた選択肢"
>
<h3 >選ばれた選択肢</h3>
{props.selectedOption && <p >{props.selectedOption}</p>}
<button onClick={props.handleClearSelectedOption}>了解</button>
</Modal>
);
export default OptionModal;
react-modal のバージョンが2.2.2とかだと問題がないのですが、本バージョンだと、上記のsetAppElement のコードがないとChromeのDevToolsで下記のような警告が見えてうっとおしいです。詳細はこちら参照。
warning.js?d96e:34 Warning: react-modal: App element is not defined. Please useModal.setAppElement(el)or setappElement={el}. This is needed so screen readers don't see main content when modal is opened. It is not recommended, but you can opt-out by settingariaHideApp={false}.
ただし、これだと、次のようなひどい見た目になってしまいます。

最終的には次のようなコードにしました。
import React from 'react';
import Modal from 'react-modal';
Modal.setAppElement("#app"); // これがないと警告が出る
const OptionModal = (props) => (
<Modal
isOpen={!!props.selectedOption}
onRequestClose={props.handleClearSelectedOption} // ESCキーやダイアログの外のクリック時
contentLabel="選ばれた選択肢"
closeTimeoutMS={200}
className="modal"
>
<h3 className="modal__title">選ばれた選択肢</h3>
{props.selectedOption && <p className="modal__body">{props.selectedOption}</p>}
<button className="button" onClick={props.handleClearSelectedOption}>了解</button>
</Modal>
);
export default OptionModal;
そして、styles/components/_modal.scss というファイルを作り、styles/styles.scss というファイルから、その他のscssファイルと共に次のようにインポートしました。
@import './components/widget';
_modal.scss の内容は次のようにしました。結構複雑ですね。
.ReactModalPortal > div {
opacity: 0;
}
.ReactModalPortal .ReactModal__Overlay {
align-items: center;
display: flex;
justify-content: center;
transition: opacity 200ms ease-in-out;
&--after-open {
opacity: 1;
}
&--before-close {
opacity: 0;
}
}
.modal {
background: $light-blue;
color: white;
max-width: 30rem;
outline: none;
padding: $l-size;
text-align: center;
&__title {
margin: 0 0 $m-size 0;
}
&__body {
font-size: 2rem;
font-weight: 300;
margin: 0 0 $l-size 0;
word-break: break-all;
}
}
ここで以下のようなクラス名がでてきます。
- ReactModalPortal
- ReactModal__Overlay
- ReactModal__Overlay–after-open
- ReactModal__Overlay–before-close
これはどうやって調べたかというと、ChromeのDevToolsのElementsビューで調べました。








ディスカッション
コメント一覧
まだ、コメントがありません