【備忘録】最速でモーダルウィンドウをNext.jsで実装する

# はじめに
この記事は **react-modal** の使い方の備忘録です。

# インストール
```console
npm install react-modal
```
**TypeScript環境下** なら **型ファイル** も取得する必要があります。
```console
npm install -D @types/react-modal
```

## 最小セットでモーダルウィンドウを実装する
```tsx:app/page.tsx
"use client";

import { useState } from "react";
import Modal from "react-modal";

Modal.setAppElement(".App");

export default function Home() {
  const [ modalIsOpen, setIsOpen ] = useState(false);

  return (
    <div className="App">
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      <Modal isOpen={modalIsOpen}>
        <button onClick={() => setIsOpen(false)}>Close Modal</button>
      </Modal>
    </div>
  );
}
```
・**Modalコンポーネント** の **isOpen Props** が **true** なら
**Modalコンポーネント** の中が **モーダルウィンドウ** として **描画** されます。
・逆に **Modalコンポーネント** の **isOpen Props** が **false** なら
**Modalコンポーネント** の中は **描画** されません。
・**Modal.setAppELement** は **モーダルウィンドウ** で隠れた部分を 指定すると良いようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/309c4190-185a-019e-0bfb-d15ff20807c8.png)

**最小セット** では **"モーダルウィンドウ"** がこのように **描画** されます。

## モーダルウィンドウの見た目を変更する
```tsx:app/page.tsx
"use client";

import { useState } from "react";
import Modal from "react-modal";

Modal.setAppElement(".App");

const modalStyle = {
  overlay: {
    position: "fixed",
    top: 0,
    left: 0,
    backgroundColor: "rgba(0,0,0,0.85)"
  },
  content: {
    position: "absolute",
    top: "5rem",
    left: "5rem",
    right: "5rem",
    bottom: "5rem",
    backgroundColor: "paleturquoise",
    borderRadius: "1rem",
    padding: "1.5rem"
  }
};

export default function Home() {
  const [ modalIsOpen, setIsOpen ] = useState(false);

  return (
    <div className="App">
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      <Modal isOpen={modalIsOpen} style={modalStyle}>
        <button onClick={() => setIsOpen(false)}>Close Modal</button>
      </Modal>
    </div>
  );
}
```
**Modalコンポーネント** の **style Props** に
**CSSを記述したオブジェクト** を渡すことで **モーダルウィンドウ** の **見た目** を **変更** します。

**CSSを記述したオブジェクト** は **overlay** と **content** という **プロパティ** を持ちます。
**overlay** には **モーダルウィンドウの外側** に **適用したいCSS** を
**content** には **モーダルウィンドウの内側** に **適用したいCSS** を
それぞれ記述します。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3348923/a61948db-a3b5-79e9-e521-74470bb2a90f.png)

実装例はこのように **描画** されます。

:::note warn
**CSSを記述したオブジェクト** に **positionプロパティ** を渡していますが、
**TypeScript環境下** だと **型エラー** が発生します。
これは **デフォルト** で **positionプロパティ** が当てられていることが関係していると思うので **指定しない** で大丈夫だと思います。
:::

## モーダルウィンドウの外側をクリックしてモーダルウィンドウを閉じる
```tsx:app/page.tsx
"use client";

import { useState } from "react";
import Modal from "react-modal";

Modal.setAppElement(".App");

const modalStyle = {
  overlay: {
    position: "fixed",
    top: 0,
    left: 0,
    backgroundColor: "rgba(0,0,0,0.85)"
  },
  content: {
    position: "absolute",
    top: "5rem",
    left: "5rem",
    right: "5rem",
    bottom: "5rem",
    backgroundColor: "paleturquoise",
    borderRadius: "1rem",
    padding: "1.5rem"
  }
};

export default function Home() {
  const [ modalIsOpen, setIsOpen ] = useState(false);

  return (
    <div className="App">
      <button onClick={() => setIsOpen(true)}>Open Modal</button>
      <Modal isOpen={modalIsOpen} style={modalStyle} onRequestClose={() => setIsOpen(false)}>
        <button onClick={() => setIsOpen(false)}>Close Modal</button>
      </Modal>
    </div>
  );
}
```
**Modalコンポーネント** の **onRequestClose Props** に渡した **callback関数** が
**モーダルウィンドウ** の **外側** を **クリック** された時に **発火** します。

**モーダルウィンドウ** の **開閉フラグ** を **callback関数内** で **falsyな値** にしておくことで
**モーダルウィンドウの外側** を **クリック** した時に
**モーダルウィンドウ** を **閉じる** ことができます。


## さいごに
学習させていただいた先・ライブラリの公式ドキュメントです。

https://reactcommunity.org/react-modal/styles/

https://gladevise.com/react-modal-usage