【React】react-modalで複数のモーダルの作り方

# はじめに
rect-modalで複数のモダールを制御するやり方が分からなかったので記事にします。

react-modalは、Reactアプリケーションで簡単にモーダルウィンドウを作成できるライブラリです。モーダルの開閉を`isOpen`プロパティで制御します。

https://github.com/reactjs/react-modal

:::note warn
警告
本記事では、`react-modal`のインストールが完了している前提で進めます。
以下のコマンドで事前にインストールしてください
```bash
$ npm install --save react-modal
```
:::

# 実装方法
## モーダルが1つの場合
```App.jsx
import { useState } from "react";
import "./App.css";
import NormalModal from "./components/NormalModal";

function App() {
  const [modalIsOpen, setisOpen] = useState(false);
  
  return (
    <>
      <div className="bg-teal-500 h-screen p-20">
        <h1 className="text-3xl font-bold underline text-center mb-8">モーダル練習</h1>
        <NormalModal modalIsOpen={modalIsOpen} setisOpen={setisOpen}>
          aaaa
        </NormalModal>
      </div>
    </>
  );
}

export default App;
```

```NormalModal.jsx
import Modal from "react-modal";

const NormalModal = ({ setisOpen, modalIsOpen, children }) => {
  return (
    <div>
      <Modal isOpen={modalIsOpen}>
        <button onClick={() => setisOpen((prev) => !prev)}>モーダルを閉じる</button>
        <div>{children}</div>
      </Modal>
      <button onClick={() => setisOpen((prev) => !prev)}>モーダルを開く</button>
    </div>
  );
};

export default NormalModal;
```

![画面収録 2024-12-07 11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3835217/e320f43f-5bbe-fcd2-a50f-03bfc4444fc0.gif)
このように簡単にモーダルを実装することができます。

## モーダルが2つの場合
ページ内にモーダルが一つの場合は上記のように実装すれば良いですが、ページの中で複数のモーダルがあることが多いと思います。いくつかの方法で試してみました。

### モーダルのコンポーネントを2つにしてみる
まずはモーダルを増やしてみました。
1つ目のモーダルには「aaaaa」が表示され２つ目のモーダルには「bbbb」が表示されていれば正しい挙動です。

```NormalModal.jsx
import { useState } from "react";
import "./App.css";
import NormalModal from "./components/NormalModal";

function App() {
  const [modalIsOpen, setisOpen] = useState(false);
  return (
    <>
      <div className="bg-teal-500 h-screen p-20">
        <h1 className="text-3xl font-bold underline text-center mb-8">モーダル練習</h1>
        <NormalModal modalIsOpen={modalIsOpen} setisOpen={setisOpen}>
          aaaa
        </NormalModal>
        <!-- コンポーネントを追加 -->
        <NormalModal modalIsOpen={modalIsOpen} setisOpen={setisOpen}>
          bbbb
        </NormalModal>
        <!-- /コンポーネントを追加 -->
      </div>
    </>
  );
}

export default App;
```

![画面収録 2024-12-07 11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3835217/f90bb233-4685-7346-61b5-80b4686351d5.gif)

このGIFでは、`NormalModal`を2つ表示していますが、1つの`state`で管理しているため、どちらをクリックしても2つ同時に開閉してしまいます。今の状態ではどのモーダルをクリックしたのか判定はできないということがわかりました。

### モーダルのごとに個別の`state`で管理する
現在はモーダルの開閉状態を一つの`state`で管理しています。モーダルごとに個別に管理してみました。

```App.jsx
import { useState } from "react";
import "./App.css";
import NormalModal from "./components/NormalModal";

function App() {
  <!-- stateをモーダルごとに変更 -->
  const [modalIsOpenA, setisOpenA] = useState(false);
  const [modalIsOpenB, setisOpenB] = useState(false);
  <!-- /stateをモーダルごとに変更 -->

  return (
    <>
      <div className="bg-teal-500 h-screen p-20">
        <h1 className="text-3xl font-bold underline text-center mb-8">モーダル練習</h1>
        <NormalModal isOpen={modalIsOpenA} setisOpen={setisOpenA}>
          aaaa
        </NormalModal>
        <NormalModal isOpen={modalIsOpenB} setisOpen={setisOpenB}>
          bbbb
        </NormalModal>
      </div>
    </>
  );
}

export default App;
```

![画面収録 2024-12-07 12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3835217/207d33e3-41af-2230-c335-2107afc40515.gif)

`state`をモーダルごとに管理するように変更するとモーダルの出しわけをすることに成功しました。

### モーダルのごとに個別の`id`で管理する
モーダルの出しわけに成功しました。しかしモーダルの数が増えていくたびに固有の`state`を作るのは現実的でないのでidを使って管理するようにしてみました。

```App.jsx
import { useState } from "react";
import "./App.css";
import NormalModal from "./components/NormalModal";

function App() {
  const [openModalId, setOpenModalId] = useState(null);

  return (
    <>
      <div className="bg-teal-500 h-screen p-20 relative">
        <h1 className="text-3xl font-bold underline text-center mb-8">モーダル練習</h1>
        <NormalModal modalId="modal1" openModalId={openModalId} setOpenModalId={setOpenModalId}>
          aaaa
        </NormalModal>
        <NormalModal modalId="modal2" openModalId={openModalId} setOpenModalId={setOpenModalId}>
          bbbb
        </NormalModal>
        <NormalModal modalId="modal3" openModalId={openModalId} setOpenModalId={setOpenModalId}>
          cccc
        </NormalModal>
      </div>
    </>
  );
}

export default App;

```
```NormalModal.jsx
import Modal from "react-modal";

const NormalModal = ({ setOpenModalId, openModalId, children, modalId }) => {
  const isOpen = openModalId === modalId;
  return (
    <div>
      <Modal isOpen={isOpen}>
        <button onClick={() => setOpenModalId(null)}>モーダルを閉じる</button>
        <div>{children}</div>
      </Modal>
      <button onClick={() => setOpenModalId(modalId)}>モーダルを開く</button>
    </div>
  );
};

export default NormalModal;
```

![画面収録 2024-12-07 15.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3835217/82fb0d12-cc9c-6b95-25d1-b4882acca40b.gif)

このように開いているモーダルの`id`を状態として管理することによって複数のモーダルを制御することができました。

# 終わりに
本記事では、`react-modal`を使った基本的なモーダル実装から、複数モーダルを効率的に管理する方法を記事にしました。

今回の実装方法を応用することで、より複雑なモーダル構成も管理しやすくなると思います。