Reactに限らずアプリケーションを構築する場合モーダルウィンドウを利用する機会が頻繁にあります。モーダルウィンドを利用する頻度が高いだけに手軽に利用できるreact-modalといったライブラリも存在します。ライブラリを利用すると短い時間で実装することができますがモーダルウィンドウの実装はReactの基本的な知識を身に付けるためのいい教材なのでReactを学び始めの方はぜひ本書を読みながら自作のモーダルウィンドウにチャレンジしてください。

本文書のモーダルウィンドウ作成から学べること

  • コンポーネントの作成
  • クリックイベント
  • useStateの使い方
  • if文の条件分岐による画面表示の切替
  • 親コンポーネントから子コンポーネントの情報の渡し方
  • 子コンポーネントから親コンポーネントの情報の渡し方
  • stopPropagationの利用方法
  • オーバーレイの設定

React Hooksを利用してモーダルウィンドウを作成する方法も公開しています。ぜひこちらも参考にしてください。

Vue.jsを学びはじめの方であればこちらがおすすめです。

モーダルウィンドウとは

モーダルウィンドウは画面中央にウィンドウが表示され、表示されたウィンドウ以外の背景を薄暗く表示させることでユーザに伝えたい内容を際立たせるための技術です。Webの世界ではさまざまな場所で利用されているので、モーダルウィンドウという言葉にピンとこない人でもこの技術に触れたことのない人はいないでしょう。あるサイトにアクセスすると急に画面中央にモーダルウィンドウが表示され消し方がわからないといった経験をしたことのある人も多いかと思います。英語ではmodalという単語を使うので検索する場合はmodalと検索することで情報を見つけることができます。

モーダルウィンドウ
モーダルウィンドウ

注意点

本文書では作成するモーダルウィンドウの見栄えは重視していません。これまでの個人的な経験では見栄えに注目した場合にCSSが複雑になりCSSの記述箇所で勉強を諦める人もいます。本文書では仕組みに注目しているのでまずは仕組みを理解した後にオリジナルのCSSの装飾を行ってください。

Reactによるモーダルウィンドウの作成

構築環境

手元のPCでもすぐに開始できるようにcdnを利用して作成を行います。任意の場所にindex.htmlを作成して以下を記述してください。cdnではreact, react-dom, babelの3つを利用しています。reactとreact-domのバージョンは17と18で動作確認しています。


<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>モーダルウィンドウでReactの基礎を学ぶ</title>
  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<style>
</style>
<body>
  <div id="root"></div>
  <script type="text/babel">

  function App() {
    return (
      <div>
        モーダルウィンドウ
      </div>
    )
  }

  ReactDOM.render(<App />, document.getElementById('root'))
  </script>
</body>
</html>

エラーもなくブラウザにモーダルウィンドウという文字列が表示されることを確認してください。

Reactの動作確認
Reactの動作確認

クリックボタンの作成

モーダルウィンドウはWEBサイトにアクセスした後に一定時間経過したら急に表示されるものから情報を登録しようとした場合にボタンをクリックすると入力フォームが表示されるものなどいろいろな箇所で利用されています。本文書ではボタンをクリックすると表示されるモーダルウィンドウを作成します。そのため最初にボタン要素を追加します。


function App() {
  return (
    <div>
      <button>Click</button>
    </div>
  )
}

画面に”Click”ボタンが表示されますがボタンを押しても何もおきません。

オーバーレイの要素を追加

オーバーレイはモーダルウィンドウが開いた時にブラウザの画面全体を覆う薄暗い幕の要素です。id=”overlay”を持つ要素を追加します。


<div>
  <button>Click</button>
  <div id="overlay"></div>
</div>

オーバーレイを表示させるためにはCSSの設定が必要となります。CSSに複数のプロパティを設定しているので一見複雑そうに見えるかもしれませんが2つのパートに分けることができるため、一つ一つ確認すればシンプルです。


<style>
#overlay{
/* 画面全体を覆う設定 */
  position:fixed;
  top:0;
  left:0;
  width:100%;
  height:100%;
  background-color:rgba(0,0,0,0.5);

  /* 画面の中央に要素を表示させる設定 */
  display: flex;
  align-items: center;
  justify-content: center;

}
</style>

overlayはfixedで固定要素として設定を行っています。画面全体にoverlayの要素を覆うためtop:0, left:0, width:100%;height:100%を設定します。postionをfixedに設定するとbutton要素よりも重ね順が上の要素となるためz-indexの設定がなくてもオーバーレイがボタンの上に表示されます。

背景色はbackground-color:rgba(0,0,0,0.5)を設定しています。透過にはopacityを利用することができますが、opacityを利用するとoverlay要素に含まれる子要素もopacityが継承されることになるのでrgba(0,0,0,0.5)を設定しています。

display:flexはフレックスボックスの設定でここでの設定はオーバーレイの中の要素すべてが画面中央に表示されるように設定しています。この設定は後ほどのoverlayの中への子要素の追加ではっきり理解することができます。

この状態でブラウザで確認すると画面全体がグレーに変わります。透明度を0.5にしているのでoverlayの要素の下にあるclickボタンを確認することができますが、クリックはできません。

オーバーレイによりブラウザ全体に幕が表示
オーバーレイによりブラウザ全体に幕が表示
overlayにz-indexをマイナス設定するとClickボタンの方が重ね順が上になるためClickボタンがクリック可能となります。
fukidashi

コンテンツの追加

設定したオーバーレイの中にコンテンツを追加します。div要素にid=”content”を設定しそのdiv要素の中にp要素とbutton要素を追加します。


<div id="overlay">
  <div id="content">
    <p>これがモーダルウィンドウです。</p>
    <p><button>close</button></p>
  </div>
</div>

styleタグに#contetのCSSを追加します。


#content{
  z-index:2;
  width:50%;
  padding: 1em;
  background:#fff;
}

モーダルウィンドウが表示されている状態を作成することができました。overlayで行ったFlexboxの設定により画面の中央に表示されます。widthを50%に設定しているのでブラウザ側の横幅の50%でコンテンツ領域が表示されます。

モーダルウィンドが表示されている状態
モーダルウィンドが表示されている状態

Modalコンポーネントの作成

ここからはReactの機能を利用して設定を行っていきます。モーダルウィンドウ部分をModalコンポーネントとしてAppコンポーネントと分割します。


function Modal(){
  return (
    <div id="overlay">
      <div id="content">
        <p>これがモーダルウィンドウです。</p>
        <p><button>close</button></p>
      </div>
    </div>
  )
}

function App() {
  return (
     <div>
       <button>Click</button>
       <Modal />
     </div>
   )
 }

ModalコンポーネントとAppコンポーネントに分割を行っただけなので表示内容はこれまでと一切変わりません。エラーがでないことを確認します。

モーダルウィンドが表示されている状態
Modalコンポーネントと分割後の表示

Modalの表示・非表示設定

Clickボタンを押すとモーダルウィンドウを表示できるように表示・非表示を切り換える方法が必要となります。表示・非表示にはif文による条件分岐を利用して”show”という変数の値によって切替を行います。

useStateフックでshowを定義します。showのデフォルト値はfalseに設定します。


const { useState } = React //cdnの利用の場合必須

function App() {
   const [show, setShow] = useState(false)
   return (
 //略
   )
}
cdnを利用している場合にuseStateを利用する場合はconst { useState } = Reactの1文が必須となります。create-react-appで作成したReactプロジェクトの場合はimportで忘れずにuseStateを指定する必要があります。
fukidashi

showの値によって内容を表示・非表示にするかは以下のように設定を行います。if文の条件にtrueを入力するとモーダルウィンドウが表示され、falseを入力すると表示されません。


function Modal() {
  if (true) {
    return (
      <div id="overlay">
        <div id="content">
          <p>これがモーダルウィンドウです。</p>
          <button>Close</button>
        </div>
      </div >
    )
  } else {
    return null;
  }
}

if文の条件をAppコンポーネントで設定したshowに変えます。しかし、showは親のコンポーネントであるAppで定義しているので下記のように利用することはできません。


function Modal() {
  if (show) {
    return (
//略

親コンポーネントから子コンポーネントに情報を渡すためにpropsを利用します。


<Modal show={show}/>

渡されたpropsをModalコンポーネントでは引数として受け取ることができます。


function Modal(props) {
  if (props.show) {
    return (
//略

Destructuring(分割代入)の構文を利用して下記のように書き換えを行います。この処理は必須ではありません。


function Modal({show}) {
  if (show) {
    return (
//略

デフォルトでshowの初期値はfalseに設定されていたのでモーダルウィンドウは表示されませんが、初期値をtrueに設定するとモーダルウィンドウが表示されます。

クリックイベント

Clickボタンをクリックしてshowの値をfalseからtrueに変更することができれば非表示のモーダルウィンドウが動的に表示されるようになります。ClickボタンにonClickイベントを設定して、setShowでshowの値をtrueに設定します。


<button onClick={() => setShow(true)}>Click</button>

Clickボタンを押す前はshowの初期値がfalseなのでモーダルウィンドウは表示されません。

Clickボタンを押す前
Clickボタンを押す前

Clickボタンを押すとモーダルウィンドウが表示されます。

モーダルウィンドが表示されている状態
モーダルウィンドが表示されている状態

onClickイベントで直接関数を設定していますが、別の関数(openModal)としてわけることも可能です。


function App() {
  const [show, setShow] = useState(false)
  const openModal = () => {
    setShow(true)
  }
  return (
    <div>
      <button onClick={openModal}>Click</button>
      <Modal show={show }/>
    </div>
  )
}

Clickボタンでモーダルウィンドウを表示させることができたので次はモーダルウィンドウを非表示にする機能を追加する必要があります。

Modalコンポーネントにあるcloseボタンをクリックするとモーダルウィンドウを非表示にしたいのでClickボタンと同様にonClickイベントを設定します。


<button onClick={() => setShow(false)}>Close</button>

Clickボタンを押してモーダルウィンドウを表示させた後closeボタンを押しても何も変化はありません。コンソールに”Uncaught ReferenceError: setShow is not defined”と表示されていることが確認できます。つまりsetShowがModalコンポーネントで定義されていないため実行することができません。

親であるAppコンポーネントで定義したshowをどのようにして変更するのでしょう。この場合はsetShowをpropsで親から子に渡し、子コンポーネントでsetShowを実行すると子で行った処理を親に伝えることが可能となります。


<Modal show={show} setShow={setShow} />

propsで渡されるのでshowと同様にsetShowを受け取ります。


function Modal({show, setShow}) {
  if (show) {
    return (
      <div id="overlay">
        <div id="content">
          <p>これがモーダルウィンドウです。</p>
          <button onClick={() => setShow(false)}>Close</button>
        </div>
      </div >
    )
  } else {
    return null;
  }
}

Clickボタンを押すとモーダルウィンドウが表示され、Closeボタンを押すとモーダルウィンドウが表示になる設定が完了しました。ここまでの設定でモーダルウィンドウは完成です。

モーダルの表示・非表示確認
kモーダルの表示・非表示確認

closeボタンを押すとモーダルウィンドウが非表示になることが確認できたので、コンテンツ以外のオーバーレイをクリックしても非表示になるように設定を行います。新たにcloseModal関数をModalコンポーネントの中に作成しています。

divのoverlayの要素にもクリックイベントを設定します。


function Modal({show, setShow}) {
  const closeModal = () => {
    setShow(false)
  }
  if (show) {
    return (
      <div id="overlay" onClick={closeModal}>
        <div id="content">
          <p>これがモーダルウィンドウです。</p>
          <button onClick={closeModal}>Close</button>
        </div>
      </div >
    )
  } else {
    return null;
  }
}

オーバーレイをクリックしてもモーダルウィンドウは表示されるようになりましたがコンテンツの白の部分をクリックしても非表示となります。

stopPropagationの設定

コンテンツの要素(id=”content”)でClickイベントが伝搬しないようにstopPropagationの設定を行います。


<div id="content" onClick={(e) => e.stopPropagation()}>

設定後はコンテンツ領域をクリックしても非表示となりません。

コンテンツに表示される中身を変更したい

コンテンツに表示される中身はModalコンポーネントに直接記述していましたが、親側のAppコンポーネントから表示させる内容を制御することができます。その場合もpropsを利用します。


<Modal show={show} setShow={setShow} content="Appから内容を変更できます" />

show, setShowと同様にcontetの中身を受け取り、表示することができます。


function Modal({show, setShow, content}) {
  const closeModal = () => {
    setShow(false)
  }
  if (show) {
    return (
      <div id="overlay" onClick={closeModal}>
        <div id="content" onClick={(e) => e.stopPropagation()}>
          <p>{content}</p>
          <button onClick={closeModal}>Close</button>
        </div>
      </div >
    )
  } else {
    return null;
  }
}

ブラウザで確認するとpropsで渡したcontentの内容が表示されることが確認できました。

propsを使って表示内容を変更
propsを使って表示内容を変更

propsのchildrenを利用することでHTMLの要素渡すことができます。モーダルウィンドウの中にはModalタグの中に挿入したHTMLの要素の内容が表示されます。


function Modal({ show, setShow, children }) {
  const closeModal = () => {
    setShow(false);
  };
  if (show) {
    return (
      <div id="overlay" onClick={closeModal}>
        <div id="content" onClick={(e) => e.stopPropagation()}>
          {children}
          <button onClick={closeModal}>Close</button>
        </div>
      </div>
    );
  } else {
    return null;
  }
}

function App() {
  const [show, setShow] = useState(false);
  return (
    <div>
      <button onClick={() => setShow(true)}>Click</button>
      <Modal show={show} setShow={setShow}>
        <p>Childrenを使っています。</p>
      </Modal>
    </div>
  );
}

シンプルなモーダルウィンドウですが、モーダルウィンドウを自作するだけでReactの各種機能の基礎知識を学ぶことができました。