Open6

[Scrap] Vue でもコンポーネントを返す hooks が実装できるか試してみた

やりたいこと

基本的には React と同じことができるようにしたい。

  1. hooks は状態を持つ
  2. hooks はコンポーネントと関数を返す
  3. コンポーネントには props を受け渡すことができる
  4. コンポーネントは hooks が返す関数の操作によって更新できる

作ったもの

CustomDialog と CustomDialog を開く操作を提供する openDialog 関数を返す useCustomDialog hooks です。
hooks 内の状態をコンポーネントに伝搬させる方法として、Ref オブジェクトを inject するようにしています。

defineComponentをuseCustomDialogの中でやればシンプルになりそう

import { defineComponent, ref } from "vue";

export const useCustomDialog = () => {
  const isOpen = ref(false);
  const openDialog = () => {
    isOpen.value = true;
  };

  const CustomDialog = defineComponent({
    props: {
      title: {
        type: String,
        required: true,
      },
    },
    setup(props) {
      return () => (
        <dialog open={isOpen.value}>
          <h1>InputValue is {props.title}</h1>
          <button
            onClick={() => {
              isOpen.value = false;
            }}
          >
            CLOSE
          </button>
        </dialog>
      );
    },
  });

  return {
    CustomDialog,
    openDialog,
  };
};

おお、ありがとうございます! 何か勘違いしていたようです!
こちらの方がいいですね👍

defineExpose を使うやり方

普通に SFC の構文使いたい場合、defineExpose を使うといけるかもと miyaoka さんに聞いたので試してみた。
以下にほとんど同じような実装が載っていたので参考に書いてみた。

https://vuejs.org/guide/typescript/composition-api.html#typing-component-template-refs

CustomDialog

SFC 内に定義した関数を defineExpose に渡している。

<script lang="ts" setup>
import { defineExpose, defineProps, ref } from "vue";

const props = defineProps<{ title: string }>();
const isOpen = ref(false);
const open = () => {
  isOpen.value = true;
};
const close = () => {
  isOpen.value = false;
};
defineExpose({
  open,
  close,
});
</script>
<template>
  <dialog :open="isOpen">
    <h1>InputValue is {{ props.title }}</h1>
    <button @click="close">CLOSE</button>
  </dialog>
</template>

useCustomDialog hooks

dialogRef 経由で open 関数を取得している。使用側で dialogRef を設定してもらうために一緒に返している。

import { ref } from "vue";
import CustomDialog from "@/components/HooksComponentView/CustomDialog.vue";

export const useCustomDialog = () => {
  const dialogRef = ref<InstanceType<typeof CustomDialog>>(null);
  const openDialog = () => {
    dialogRef.value?.open();
  };

  return {
    dialogRef,
    CustomDialog,
    openDialog,
  };
};

呼び出し側

呼び出し側では、CustomDilaog を操作可能にするために dialogRefCustomDialog に設定してあげる必要がある。ref の設定は必要なものの、シンプルで書きやすいかも。

<script setup lang="ts">
import { ref } from "vue";
import { useCustomDialog } from "@/components/HooksComponentView/useCustomDialog";

const { openDialog, CustomDialog, dialogRef } = useCustomDialog();
const inputValue = ref("VueJS");
</script>
<template>
  <div class="bg-gray-500 w-full h-screen p-4">
    <input class="block p-2" v-model="inputValue" />
    <button class="bg-white rounded p-2 mt-2" @click="openDialog">
      Open Dialog
    </button>
    <CustomDialog ref="dialogRef" :title="inputValue" />
  </div>
</template>

ログインするとコメントできます