Closed6
[Scrap] Vue でもコンポーネントを返す hooks が実装できるか試してみた
背景
以下のツイートを見て、React でコンポーネントを返す hooks を書いてみたところかなりしっくりきたので、仕事で使っている Vue でも同じことができないか試してみました。
やりたいこと
基本的には React と同じことができるようにしたい。
- hooks は状態を持つ
- hooks はコンポーネントと関数を返す
- コンポーネントには props を受け渡すことができる
- コンポーネントは 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 さんに聞いたので試してみた。
以下にほとんど同じような実装が載っていたので参考に書いてみた。
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
を操作可能にするために dialogRef
を CustomDialog
に設定してあげる必要がある。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>
このスクラップは2022/07/19にクローズされました