Next.js + Tailwind CSSで使えるアラートモーダル

# Next.js + Tailwind CSSで使えるアラートモーダル

簡単にアラートを出せるモーダルを手作りしてみた。

**アラートだけのモーダル**
![スクリーンショット 2023-09-02 17.39.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1279682/ac0dc7ec-1e48-9a46-dfa2-d6931e617e9f.png)

**確認モーダル**
OK ボタンを押すと処理が走る。
![スクリーンショット 2023-09-02 17.38.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1279682/11abf2f0-3d6c-5881-8e62-263808571a66.png)


# コード

```tsx: AlertModalManager.tsx
import { Dispatch, SetStateAction, useState } from 'react';
import ReactDOM from 'react-dom';

export interface AlertModalProps {
  title?: string;
  message?: string;
  onOk?: () => void;
}

let setAlertModalContentGlobal: Dispatch<SetStateAction<AlertModalProps | null>>;

export function showAlertModal(content: AlertModalProps) {
  setAlertModalContentGlobal(content);
}

export function closeAlertModal() {
  setAlertModalContentGlobal(null);
}

export const AlertModal: React.FC<AlertModalProps> = ({ title, message, onOk }) => {
  return (
    <div className='ma fixed left-0 top-0 flex h-full w-full items-center justify-center bg-black bg-opacity-50'>
      <div className='max-w-lg rounded-lg bg-white p-8'>
        {title && <h2 className='mb-4 text-xl font-bold'>{title}</h2>}
        {message && <p className='mb-4 whitespace-pre-line'>{message}</p>}
        {onOk === undefined ? (
          <div className='flex justify-center'>
            <button
              onClick={closeAlertModal}
              className='w-full rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600'
            >
              Close
            </button>
          </div>
        ) : (
          <div className='flex justify-between'>
            <button onClick={closeAlertModal} className='rounded bg-gray-300 px-4 py-2 hover:bg-gray-400'>
              Cancel
            </button>
            <button
              onClick={() => {
                onOk();
                closeAlertModal();
              }}
              className='rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600'
            >
              OK
            </button>
          </div>
        )}
      </div>
    </div>
  );
};

export const AlertModalManager = () => {
  const [alertModalContent, setAlertModalContent] = useState<AlertModalProps | null>(null);

  setAlertModalContentGlobal = setAlertModalContent;

  if (!alertModalContent) return null;

  return ReactDOM.createPortal(<AlertModal {...alertModalContent} />, document.body);
};

```

# 使い方

コードのどこかに `<AlertModalManager />` を入れる。

```tsx
import { AlertModalManager } from './AlertModalManager';

function MyApp() {
  return (
    <>
        :
      <AlertModalManager />
    </>
  );
}

export default MyApp;
```

コードの任意の場所から呼び出す。
```tsx
  const showAlert = () => {
    showAlertModal({
      title: 'タイトル',
      message: 'ここにメッセージが入ります。',
    });
  };
```
![スクリーンショット 2023-09-02 17.39.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1279682/ac0dc7ec-1e48-9a46-dfa2-d6931e617e9f.png)
onOkを設定しないと `Closeボタン` のみ表示される。


```tsx
  const showAlert = () => {
    showAlertModal({
      title: 'タイトル',
      message: 'ここにメッセージが入ります。',
      onOk: () => {
        hoge("hoge");
      },
    });
  };
```

onOkを設定すると `Cancelボタン` と `OKボタン` が表示される。
`OKボタン` を押すと指定した `hoge("hoge");` が実行される。
![スクリーンショット 2023-09-02 17.38.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1279682/11abf2f0-3d6c-5881-8e62-263808571a66.png)


# 感想
tostifyっぽく使えるものを自分で作れて満足。