useConfirmDialogで確認ダイアログをスマートに実装する

# 前置き

今回はVue3で確認ダイアログを実装するのにおすすめのフックを紹介します。

https://vueuse.org/core/useConfirmDialog/



# 素直な実装例

## コンポーネント全体
```vue
<template>
  <div>
    <button :disabled="showDialog" @click="onClick">
      確認
    </button>
    <div v-if="showDialog">
      ダイアログ表示
      <button @click="onConfirm">
        OK
      </button>
      <button @click="onCancel">
        Cancel
      </button>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
    const showDialog = ref(false);
    function onClick() {
      showDialog.value = true;
    }

    function onConfirm() {
      showDialog.value = false;

      // 確認後処理
    }

    function onCancel() {
      showDialog.value = false;
    }

    return {
      showDialog,
      onClick,
      onConfirm,
      onCancel,
    };
  },
});
</script>
```

## 確認ダイアログの制御部分をピックアップ

```ts
    const showDialog = ref(false);
    function onClick() {
      showDialog.value = true;
    }

    function onConfirm() {
      showDialog.value = false;

      // 確認後処理
    }

    function onCancel() {
      showDialog.value = false;
    }
```

ダイアログ制御のためにだいたい４つの定義をするのではないでしょうか？
- showDialog: 表示制御のためのフラグ
- onClick: ボタンクリック
- onConfirm: OKが押されたときのメソッド
- onCancel: キャンセルが押されたときのメソッド

# useConfirmDialogを使ったスマートな実装例
```ts
    const dialog = useConfirmDialog();
    async function onClick() {
      const { isCanceled } = await dialog.reveal();

      if (isCanceled) return;

      // 確認後処理
    }
```

これだけです。
独自の確認ダイアログでも`window.confirm`のように１行で書けます。
ボタンを押してからメインの処理まで一つの関数にまとまり可読性も良いです。


## これだけ知っておけば使えるプロパティ/メソッド

|プロパティ/メソッド  |型 |説明  |
|---|---|---|
|reveal |(data?: RevealData) => Promise\<UseConfirmDialogRevealResult\<ConfirmData, CancelData\>\>  |確認ダイアログを開始するメソッド。 |
|confirm  |(data?: ConfirmData) => void |OKでダイアログを終了する |
|cancel  |(data?: CancelData) => void |キャンセルでダイアログを終了する |
|isRevealed  |ComputedRef\<boolean>|確認ダイアログ表示中の状態 |

## コンポーネント全体

```vue
<template>
  <div>
    <button :disabled="dialog.isRevealed.value" @click="onClick">
      確認
    </button>
    <div v-if="dialog.isRevealed.value">
      ダイアログ表示
      <button @click="dialog.confirm">
        OK
      </button>
      <button @click="dialog.cancel">
        Cancel
      </button>
    </div>
  </div>
</template>

<script lang="ts">
import { useConfirmDialog } from '@vueuse/core';
import { defineComponent } from 'vue';

export default defineComponent({
  setup() {
    const dialog = useConfirmDialog();
    async function onClick() {
      const { isCanceled } = await dialog.reveal();

      if (isCanceled) return;

      // 確認後処理
    }

    return {
      dialog,
      onClick,
    };
  },
});
</script>
```


# 私の実用例

私はさらに`window.confirm`のように使えるようにメッセージも渡せる機能を作りました。

```vue
<template>
  <div>
    <button :disabled="dialog.isRevealed.value" @click="onClick">
      確認
    </button>
    <MessageDialog :dialog="dialog"/>
  </div>
</template>

<script lang="ts">
import { useMessageDialog } from '@/hooks/message_dialog.ts';
import { defineComponent } from 'vue';

export default defineComponent({
  setup() {
    const dialog = useMessageDialog();
    async function onClick() {
      const { isCanceled } = await dialog.confirm('メッセージ');

      if (isCanceled) return;

      // 確認後処理
    }

    return {
      dialog,
      onClick,
    };
  },
});
</script>

```

ポイントを紹介します。
- useMessageDialogフック： useConfirmDialogをラップして、ダイアログに表示するメッセージデータを持てるようにしたカスタムフックです。
- confirmメソッド： revealメソッドをラップして、独自のメッセージを渡せるようにします。
- MessageDialogコンポーネント： useMessageDialogとセットで利用するコンポーネントです。メッセージデータを取り出して表示し、OKボタンとキャンセルボタンを持ちます。