📚
【Vue3】Props をオブジェクトや配列として受け取って一部変更すると、親コンポーネントのデータも同時に変更される
はじめに
Vue において props は readOnly
= 変更不可とされています。
子コンポーネントから親コンポーネントのデータを書き換えるためには、emit
を使う必要があるとのことです。
しかし、props が配列やオブジェクトの場合、子コンポーネントのプロパティを変更するだけで emit
を使わずとも親コンポーネントの値が変更されてしまいます。
環境
- Google Chrome
- M1 Mac
- Vue3, Vite
- TypeScript, JavaScript 両方で確認
変更される条件
- Props をオブジェクト、あるいは配列として定義し、子コンポーネントで受け取る
- オブジェクトの一つのプロパティ (今回の場合は
firstname
) を変更する
App.vue
<script setup lang="ts">
import { ref } from 'vue';
import ChildComponent from './components/ChildComponent.vue';
let user = ref({
firstname: 'Taro',
lastname: 'Sato',
});
</script>
<template>
<ChildComponent :user="user" />
親コンポーネントの中身: {{ user.firstname }}
</template>
components/ChildComponent.vue
<script setup lang="ts">
import { computed, ref } from 'vue';
const props = defineProps({
user: Object,
});
// ref()に props の値を入れると、初期値を props の値にできる
const name = ref(props.user);
const firstname = ref(props.user.firstname);
const changeFirstname = function () {
name.value.firstname = 'Hanako';
};
</script>
<template>
<div>
<span>オブジェクトの一部として変更: </span>
<input type="text" v-model="name.firstname" />
</div>
<div>
<span>プリミティブ値として変更: </span>
<input type="text" v-model="firstname" />
</div>
<div>
<span>関数からオブジェクトの一部を変更: </span>
<button @click="changeFirstname">firstname を変える</button>
</div>
<div>子コンポーネントのprops の値: {{ user.firstname }}</div>
</template>
子コンポーネントを変更すると親コンポーネントの値も変更されています。
終わりに
僕はこの仕様にぶつかった時に「なんでこうなるんだ……?」となって数時間溶かしてしまったので、同じようにぶつかった人を救うためにこの記事を書きました。
解決した後に適切な言葉で検索したところ、この記事の内容は公式 doc に書いてありました。なんだと……。
多くの場合でベストプラクティスではないので避けるべし、とのことです。
Discussion