Vue3で子コンポーネントの引数プロパティで渡したオブジェクトの値を変える方法

Vue3で子コンポーネントの引数プロパティで渡したオブジェクトの値を変える方法

Vue3+composition APIで親コンポーネントから子コンポーネントへプロパティ (props)経由で渡した値を子コンポーネントで変更する方法です。
プリミティブな型ではなくオブジェクトの場合の対応方法です。

子が引数で受け取った値を書き換えることは非推奨

Reactでも同様ですが、Vueでも単方向データフローを推奨しています。
親から子にかけてデータが流れ、親が更新されれば子も変更され、その逆はありません。
守らなくても動きますが、子でデータの書き換えを行い出すと どこで値が書き変わっているのか分からなくなる ため、非推奨とされています。

コンポーネントの詳細 単方向データフロー
https://v3.ja.vuejs.org/guide/component-props.html#%E5%8D%98%E6%96%B9%E5%90%91%E3%83%86%E3%82%99%E3%83%BC%E3%82%BF%E3%83%95%E3%83%AD%E3%83%BC

オブジェクトを渡す

プリミティブなStringやNumberをプロパティで渡し、子で値を書き換えようとするとコンソールに警告が表示されます。
しかし、オブジェクトを渡してオブジェクトのプロパティを書き換えても警告は表示されません。
そして、オブジェクトなので値コピーではなく参照が渡っているので子で書き換えると親の値が変わります。

一見、警告も出ないし問題ないように見えますが、先の単方向データフローに違反しています。

サンプルコード

Code Sandbox
https://codesandbox.io/s/sad-cdn-seur1?file=/src/main.js

<template>
  <h2>Parent</h2>
  name: {{ person.name }} <br />
  age: {{ person.age }}

  <hr />

  <h2>Child Badパターン</h2>
  <ChildBad :person="person" />

  <hr />

  <h2>Child Goodパターン</h2>
  <ChildGood v-model:name="person.name" v-model:age="person.age" />
</template>

<script>
import { reactive } from "vue";
import ChildBad from "./components/ChildBad.vue";
import ChildGood from "./components/ChildGood.vue";
export default {
  name: "App",
  components: {
    ChildBad,
    ChildGood,
  },
  setup() {
    const person = reactive({
      name: "john",
      age: 20,
    });

    return { person };
  },
};
</script>

Bad

<template>
  name: <input type="text" v-model="person.name" /> age:
  <input type="number" v-model="person.age" />
</template>

<script>
export default {
  name: "ChildBad",
  props: {
    person: Object,
  },
};
</script>

Good

<template>
  name:
  <input
    type="text"
    :value="name"
    @input="$emit('update:name', $event.target.value)"
  />
  age:
  <input
    type="number"
    :value="age"
    @input="$emit('update:age', $event.target.value)"
  />
</template>

<script>
export default {
  name: "ChildGood",
  props: {
    name: String,
    age: Number,
  },
  emits: ['update:name', 'update:age'],
};
</script>

 

プログラミングカテゴリの最新記事