Vue.jsで次のようなエラーが発生した時の原因と対処法についてまとめています。
原因
infinite update loopは、更新が無限に行われていることを示しています。テンプレートを描画中にdataが変更されると発生します。
具体的には、v-forの中でdataを2回以上書き換える処理をすると発生します。
dataとは?
scriptタグのexport defaultで指定しています。そのテンプレート内におけるグローバル変数のようなもので、1つの値しか持てません。
<script>
export default {
data(){
return {
arr: ['aaa', 'bbb', 'ccc', 'ddd'],
isActive: false,
}
}
}
</script>
発生例
例えば、v-forで4回ループを回す時に、dataで定義した、isActiveというプロパティを変更しようとすると、2回目の変更で発生します。
<template>
<div>
<div v-for="(val, idx) in arr" :key=idx>
<div>{{ setStatus(idx) }}</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
arr: ['aaa', 'bbb', 'ccc', 'ddd'],
isActive: false,
}
},
methods:{
setStatus(i){
if ( i == 1 ){
this.isActive = true
} else if (i == 3){
this.isActive = false
}
return this.isActive
}
}
}
</script>
<style>
</style>
if ( i == 1 ){ this.isActive = true }
1回目のループで、this.isActiveをtrueにします。この処理のみであればエラーは発生しません。
残りのループでも、一意の isActive=true というデータを呼び出すだけです。
else if (i == 3){ this.isActive = false }
この条件に引っかかったタイミングで無限ループが発生します。
本来1つしかない isActive を更に変更しようとするため、テンプレート全体のレンダリングがかかります。
すると、処理が再度行われ、ループします。
対処法
無限ループを防止するには、v-forの中でdataを変更する処理を発生させないことです。
ここでは、methodを修正する方法と、watchを使う方法を紹介します。
methodを修正する
methodの中で、dataの値を変更する、this.〇〇という処理をなくします。
実例
methods:{
setStatus(i){
if ( i == 1 ){
this.isActive = true
} else if (i == 3){
this.isActive = false
}
return this.isActive
}
}
↓
methods:{
setStatus(i){
if ( i == 1 ){
return true
} else if (i == 3){
return false
}
return this.isActive
}
}
これでエラーは発生しなくなります。
watchを使う
watchを使って、ループで回す配列を先に作り替える方法もあります。
変更する箇所は3点です。
- methodsの処理をwatchに変更する。
- テンプレートのメソッドを変数に変更する。
methodsの処理をwatchに変更する
まずは、methodsで行っていた処理を、watchに変更して、先にループで出力するデータを作ってしまいます。
methods:{
setStatus(i){
if ( i == 1 ){
this.isActive = true
} else if (i == 3){
this.isActive = false
}
return this.isActive
}
}
↓
watch:{
arr:{
immediate: true,
handler(val){
let res = []
res = val.map( (elem, i) => {
if ( i == 1 ){
return true
} else if (i == 3){
return false
}
return this.isActive
})
this.result = res
}
}
}
immediate: true
ページが描画されたタイミングで処理を行う時に指定します。これがない場合は、指定した値に変更があったタイミングで処理が行われます。
handler(val){ 処理 }
watchの決まった書き方です。変数valには、監視しているデータが入ります。変数名は何でもいいです(val以外でも問題ない)
なお、第1引数に変更後のデータ、第2引数に変更前のデータが入ります。
今回の処理時点では、watchを初回のタイミングで実施しているので、第1引数も、第2引数も同じ内容になります。
let res = []
mapの処理結果を格納する配列を定義します。これを定義しておかないと、そんな変数はないというエラーが発生します。
map( (elem, i) => { 処理 }
配列やオブジェクトの要素をひとつづつ取り出し、処理を加えて、配列を作り直します。
第1引数には取り出した要素、第2引数にはインデックス番号が入ります。変数名は何でもいいです。
this.result = res
mapで処理した結果を、新たなプロパティ result に代入しています。
この処理の結果、result = [ false, true, false, false ]
となります。
テンプレートのメソッドを変数に変更する
次に作成した配列 result を出力するように、テンプレートを書き換えます。
<template>
<div>
<div v-for="(val, idx) in arr" :key=idx>
<div>{{ setStatus(idx) }}</div>
</div>
</div>
</template>
↓
<template>
<div >
<div v-for="(val, idx) in result" :key=idx>
<div>{{ val }}</div>
</div>
</div>
</template>
以上で完了です。
v-forの中ではデータの変更を行わないので、You may have an infinite update loop in a component render function.が発生することがありません。
watchのNG例
watchの中で、this.arr を直接書き換えると、arrが変更されるたびに処理が走るので、infinite update loopが発生します。
watch:{
arr:{
immediate: true,
handler(val){
this.arr = val.map( (elem, i) => {
if ( i == 1 ){
return true
} else if (i == 3){
return false
}
return this.isActive
})
}
}
}
this.arr = でdataで定義しているarrが変更され、またwatchの同じ処理が走ります。