Vue.js 親コンポーネントの値を変更すると子コンポーネントの値が戻る場合

どういう時に発生する?

propsで親コンポーネントから受け取った変数を子コンポーネントでv-modelでバインディングして
更に、親の他の変数をpropsで受け取るようにしている場合に発生する。

つまり、props中の変数をバインディングして変更しても
親コンポーネントの再レンダリングのたびにpropsの値が上書きされ
子コンポーネントでの変更がかき消されるように見えてしまう。

例えば

親コンポーネントに、is_checkというチェックボックスがあり
子コンポーネントに、count_initという初期値とis_checkをバインディングで渡して
受け取った子コンポーネントがpropsのまま、count_initを編集した場合
親コンポーネントのis_checkを変更した時、親コンポーネントの値が再度コンポーネントに渡され
子コンポーネントでの変更が破棄されてしまう。

<body>
    <div id="app">
        <button-counter count_init="100" v-bind:is_check="is_check"></button-counter>
        <input type="checkbox" v-model="is_check">連動
    </div>

<script>
$(function(){

	var bus = new Vue();

    // 子コンポーネント
    Vue.component('button-counter', {
    props: [
        'count_init',
        'is_check',
    ],
    data: function () {
    },
    methods: {
        child_add: function(){
            this.count_init++;
        },
    },
    template: '<div><input v-model="count_init"><button v-on:click="child_add">add</button></div>'
    })

    // 親コンポーネント
    new Vue({
        el: '#app',
        data: {
            is_check: true
        }
    })

});
</script>

解決策

子コンポーネントで、propsの値を直接更新するのではなく、子コンポーネントのdataに格納してから
その変数を更新するようにすればOK。

<body>
    <div id="app">
        <button-counter count_init="100" v-bind:is_check="is_check"></button-counter>
        <input type="checkbox" v-model="is_check">連動
    </div>

<script>
$(function(){

	var bus = new Vue();

    // 子コンポーネント
    Vue.component('button-counter', {
    props: [
        'count_init',
        'is_check',
    ],
    data: function () {
        return {
            count: this.count_init
        }
    },
    methods: {
        child_add: function(){
            this.count++;
        },
    },
    template: '<div><input v-model="count"><button v-on:click="child_add">add</button></div>'
    })

    // 親コンポーネント
    new Vue({
        el: '#app',
        data: {
            is_check: true
        }
    })

});
</script>

ちなみに、親コンポーネントから子コンポーネントに渡す変数にバインディングされた状態の変数がなければ正しく(値が元に戻るということはない)動作するけど、多分、propsの値を更新するというようなことはやってはいけないことなんだと思う。

以下、2つのどちらかなら正しく動作する

1.is_checkを子コンポーネントに渡さない
<button-counter count_init="100"></button-counter>

2.渡すけど、v-bindではない
<button-counter count_init="100" is_check="is_check"></button-counter>

正しく動作するからついついやってしまってることがありそうだ。
バインディングされ、かつ変更された変数だけが子コンポーネントに影響を受けると思っていたし
まぁ、わりと早く気付けて良かった。

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です