【Vue.js】無限ループエラーの原因と対処法:[Vue warn]: You may have an infinite update loop in a component render function.

vue-js-prograshi(プロぐらし)-kv Vue.js
記事内に広告が含まれていることがあります。

Vue.jsで次のようなエラーが発生した時の原因と対処法についてまとめています。

エラー内容

[Vue warn]: You may have an infinite update loop in a component render function.

vue.runtime.esm.js:638 [Vue warn]: You may have an infinite update loop in a component render function.

found in

—> at app/javascript/roop.vue


原因

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 を更に変更しようとするため、テンプレート全体のレンダリングがかかります。

すると、処理が再度行われ、ループします。

point

無限ループが発生する状況に陥りますが、Vueには、consoleに警告を発生してループをストップする安全処理が組み込まれているため、何もできなくなるということはありません。


コードを修正すれば警告を回避することができます。


対処法

無限ループを防止するには、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の処理結果を格納する配列を定義します。これを定義しておかないと、そんな変数はないというエラーが発生します。

エラー例

vue.runtime.esm.js:638 [Vue warn]: Error in callback for immediate watcher “arr”: “ReferenceError: res is not defined”


map( (elem, i) => { 処理 }

配列やオブジェクトの要素をひとつづつ取り出し、処理を加えて、配列を作り直します。

第1引数には取り出した要素、第2引数にはインデックス番号が入ります。変数名は何でもいいです。


this.result = res

mapで処理した結果を、新たなプロパティ result に代入しています。

この処理の結果、result = [ false, true, false, false ]となります。


watchの書き方

Vueのwatchオプションには書き方がいくつかあります。

省略形

最もシンプルな書き方です。監視対象のaはdataで定義してある必要があります。
この場合、immediateなどのオプションは追加できません。

watch:{
  a(val, oldVal) {
    処理
  }
}


オプションがある場合

immediateやdeepなどのオプションを追加する場合は、handlerを使います。

watch:{
    a: {
      handler: (val, oldVal) { 処理 },
      deep: true,
      immediate :true
    },
}


(参考)Vue.js公式 vue API watch


テンプレートのメソッドを変数に変更する

次に作成した配列 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.が発生することがありません。

tips

v-forを使う時に、:key=ユニークな値 を指定しないと、VSCodeでエラーとみなされる場合があります。

▼ :key なし (エラー)

▼ :keyあり ユニークでない値を指定(エラー)

▼ :keyあり index番号を指定(OK)

:key= val.active のように、各要素で固有の値を指定することもできます。


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の同じ処理が走ります。

タイトルとURLをコピーしました