kama0244
@kama0244 (カマ ちゃん)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Vue.js 「propを直接変異させないで」というエラーについて

Q&A

Closed

解決したいこと

点数計算アプリを制作しました。
アプリ自体は意図通りに動作しています。
ただし、ある操作をすると、デベロッパーツールのコンソールにエラーメッセージが表示されてしまいます。

発生している問題・エラー

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "value"

フープインやネットインの「☓」ボタンを押すと、上記のエラーメッセージが表示されます。
propsのvalueを子で変更しないで、という意味かと思います。
コンソールにエラーメッセージは表示されていますが、動作は意図した通りに出来ています。

※該当するソースコードにはCSSが適用されていないので、表示が崩れています。

該当するソースコード

<!-- HTML -->
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">

    <!-- Vue.js(開発用) -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <table class="table table-bordered">
        <thead class="thead-light">
            <tr>
                <th class="col-sm"></th>
                <th class="col-lg">ゲート1</th>
                <th class="col-lg">ゲート2</th>
                <th class="col-lg">ゲート3</th>
                <th class="col-lg">ゲート4</th>
                <th class="col-lg">ゴール</th>
                <th class="col-md">総打数</th>
                <th class="col-md">加算点</th>
                <th class="col-md">Total</th>
            </tr>
        </thead>
        <tbody class="counter">
            <tr
                v-for="(row, index) in rows"
                :key="index"                
            >
                <th class="col-sm">{{ row.name }}</th>
                <td class="bg-light">
                    <counter-hit v-model="row.countGate1"></counter-hit>
                    <hoop-in v-model="row.inGate1"></hoop-in>
                </td>
                <td>
                    <counter-hit v-model="row.countGate2"></counter-hit>
                    <hoop-in v-model="row.inGate2"></hoop-in>
                </td>
                <td class="bg-light">
                    <counter-hit v-model="row.countGate3"></counter-hit>
                    <hoop-in v-model="row.inGate3"></hoop-in>
                </td>
                <td>
                    <counter-hit v-model="row.countGate4"></counter-hit>
                    <hoop-in v-model="row.inGate4"></hoop-in>
                </td>
                <td class="bg-light">
                    <counter-hit v-model="row.countGoal"></counter-hit>
                    <hoop-in v-model="row.inGoal">ネットイン</hoop-in>
                </td>
                <td>
                    <div class="sum">{{ sum(index) }}</div>
                </td>
                <td>
                    <div class="addition">{{ addition(index) }}</div>
                </td>
                <td>
                    <div class="total">{{ sum(index) + addition(index) }}</div>
                </td>
            </tr>
        </tbody>
    </table>

    <!-- Test.js -->
    <script src="test.js"></script>
</body>
</html>
// JavaScript
const counterHit = {
    props: ['value'],
  methods: {
        countUp() {
            this.$emit('input', this.value + 1);
        },
        countDown() {
            if ( this.value > 0 ) {
                this.$emit('input', this.value - 1);
            }
        },
    },
    template: `
        <form>
            打数
            <div class="form-group input-group">
                <div class="input-group-prepend">
                    <button type="button" @click="countUp" class="btn btn-primary btn-up">+</button>
                </div>
                <input type="number" v-model="value" min="0" disabled class="form-control">
                <div class="input-group-append">
                    <button type="button" @click="countDown" class="btn btn-secondary btn-down">-</button>
                </div>
            </div>
        </form>
    `,
};

const hoopIn = {
    props: ['value'],
    methods: {
        hoopIn() {
            if(this.value === "") {
                this.$emit('input', this.value + "");
            }
        },
        remove() {
            if(this.value === "") {
                this.$emit('input', this.value = "");
            }
        },
    },
    template: `
        <form>
            <slot>フープイン</slot>
            <div class="form-group input-group">
                <div class="input-group-prepend">
                    <button type="button" @click="hoopIn" class="btn btn-info btn-in">○</button>
                </div>
                <input type="text" v-model="value" disabled class="form-control">
                <div class="input-group-append">
                    <button type="button" @click="remove" class="btn btn-secondary btn-remove">☓</button>
                </div>
            </div>
        </form>
    `,
};

new Vue({
    el: '.counter',
    data() {
        return {
            rows: [
                {
                    name: 'A',
                    countGate1: 0,
                    countGate2: 0,
                    countGate3: 0,
                    countGate4: 0,
                    countGoal: 0,
                    inGate1: "",
                    inGate2: "",
                    inGate3: "",
                    inGate4: "",
                    inGoal: "",
                },
                {
                    name: 'B',
                    countGate1: 0,
                    countGate2: 0,
                    countGate3: 0,
                    countGate4: 0,
                    countGoal: 0,
                    inGate1: "",
                    inGate2: "",
                    inGate3: "",
                    inGate4: "",
                    inGoal: "",
                },
            ],
        };
    },
    methods: {
        sum(index) {
            return (
                this.rows[index].countGate1 +
                this.rows[index].countGate2 +
                this.rows[index].countGate3 +
                this.rows[index].countGate4 +
                this.rows[index].countGoal
            );
        },
        addition(index) {
            let additionGate1 = 0;
            let additionGate2 = 0;
            let additionGate3 = 0;
            let additionGate4 = 0;
            let additionGoal = 0;

            if(this.rows[index].countGate1 === 1 && this.rows[index].inGate1 === "") {
                additionGate1 = -3;
            } else if(this.rows[index].countGate1 === 1) {
                additionGate1 = -2;
            } else if(this.rows[index].inGate1 === "") {
                additionGate1 = -1;
            }

            if(this.rows[index].countGate2 === 1 && this.rows[index].inGate2 === "") {
                additionGate2 = -3;
            } else if(this.rows[index].countGate2 === 1) {
                additionGate2 = -2;
            } else if(this.rows[index].inGate2 === "") {
                additionGate2 = -1;
            }

            if(this.rows[index].countGate3 === 1 && this.rows[index].inGate3 === "") {
                additionGate3 = -3;
            } else if(this.rows[index].countGate3 === 1) {
                additionGate3 = -2;
            } else if(this.rows[index].inGate3 === "") {
                additionGate3 = -1;
            }

            if(this.rows[index].countGate4 === 1 && this.rows[index].inGate4 === "") {
                additionGate4 = -3;
            } else if(this.rows[index].countGate4 === 1) {
                additionGate4 = -2;
            } else if(this.rows[index].inGate4 === "") {
                additionGate4 = -1;
            }

            if(this.rows[index].countGoal === 1 && this.rows[index].inGoal === "") {
                additionGoal = -4;
            } else if(this.rows[index].countGoal === 1) {
                additionGoal = -2;
            } else if(this.rows[index].inGoal === "") {
                additionGoal = -2;
            }

            return (
                additionGate1 + 
                additionGate2 +
                additionGate3 + 
                additionGate4 +
                additionGoal
            );
        },
    },
    components: {
        'counter-hit': counterHit,
        'hoop-in': hoopIn,
    },
});

自分で試したこと

hoopInのremoveメソッドでthis.valueへの値を渡す方法が良くないのかと。
this.value + "" にしてみたりしたのですがダメでした。
Vueのコンポーネントでpropsの中身を子で変更してはダメなことは知識としては分かっているのですが、コードをどのように直せば良いのか分かりません。

0

1Answer

hoopInthis.value"" なら "○" にして、 removethis.value"○" なら "" にする処理ですよね?
でしたら、

        hoopIn() {
            if(this.value === "") {
                this.$emit('input', "");
            }
        },
        remove() {
            if(this.value === "") {
                this.$emit('input', "");
            }
        },

で良いのではないでしょうか。

もっと言ってしまえば、this.value"""○" のどちらかの状態しか取らないのなら、

        hoopIn() {
            this.$emit('input', "");
        },
        remove() {
            this.$emit('input', "");
        },

で十分に見えますが

1Like

Comments

  1. @kama0244

    Questioner

    回答ありがとうございます。

    エラーメッセージが表示されなくなりました。
    this.valueが必要無かったのですね。

Your answer might help someone💌