Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
6
Help us understand the problem. What is going on with this article?
@amuyikam

You may have an infinite update loop in a component render function. 問題の調査

tl;dr

  • Vueでconsoleに [Vue warn]: You may have an infinite update loop in a component render function. が表示される
  • render中にdataを変更してしまい、これによって再度renderが呼ばれることが原因
  • Array.prototype.sort() は対象配列を変更する破壊的メソッドなので注意が必要

概要

Vueはモデルが変更されるとリアクティブにビューを変更してくれるので便利なのですが、利用に際して気をつけなければならないこともあります。
その一つが、rendering時の無限ループです。

rendering時の無限ループは次のようなものです。
1. renderが実施される
2. render中にモデル(data)が変更される
3. モデルの変更によって再度renderが実施される
4. render中にモデルが変更される
5. モデルの変更によって再度renderが実施される
...

rendering時に無限ループが発生した場合、次のようなwarningをconsoleに出力して無限ループを止めてくれる機能1がVueにあるため極端に恐れることはないのですが、意図していない動作となるためこのwarningが出ていたら対処するのが吉です。

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

found in

---> <App> at src/App.vue
       <Root>

update loopが起きるパターン

templateでdataを変更している

一番かんたんな例です。
template中でcounter++とすることでcounterの値を変更しています。

<template>
  <div id="app">
    <div>{{counter++}}</div>
  </div>
</template>

<script>
export default {
  name: 'app',
  data: () => {
    return {
      counter: 0,
    }
  },
}
</script>

解決方法

template内でdataを変更しないようにします。
基本的に、変更が必要なケースはないはずです。(あったら教えて下さい)

methodでdataを変更している

render中に利用するmethodがdataを変更していても無限ループが起きます。

<template>
  <div id="app">
    <div>{{count()}}</div>
  </div>
</template>

<script>
export default {
  name: 'app',
  data: () => {
    return {
      counter: 0,
    }
  },
  methods: {
    count() {
      this.counter++
      return this.counter
    }
  }
}
</script>

解決方法

データを変更する可能性のあるメソッドが描画で呼ばれるのが間違いなので、メソッドの適切な分割をします。

computedでdataを変更している

<template>
  <div id="app">
    <div>{{count()}}</div>
  </div>
</template>

<script>
export default {
  name: 'app',
  data: () => {
    return {
      counter: 0,
    }
  },
  computed: {
    count() {
      return this.counter++
    }
  }

}
</script>

なお、eslint-plugin-vue を使っている場合はエラーで止めてくれます。

Failed to compile.

./src/App.vue
Module Error (from ./node_modules/eslint-loader/index.js):
error: Unexpected side effect in "count" computed property (vue/no-side-effects-in-computed-properties) at src/App.vue:17:14:
  15 |   computed: {
  16 |     count() {
> 17 |       return this.counter++
     |              ^
  18 |     }
  19 |   }
  20 | 

1 error found.

解決方法

dataに変更を加えないように修正します。

  computed: {
    count() {
      return this.counter + 1
    }
  }

配列dataに破壊的メソッドを使用してしまっている

1番目のパターンと同じです。
配列データを並べ替えて表示するときにArray.prototype.sort()を利用すると思いますが、これは元の配列を変更する破壊的メソッドです。つまり、array.sort() によって並び替えられた新しい配列が返されるのではなくarray自身が並び替えられて返されます。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

sort()の他にはreverse()等も同様。
https://vuejs.org/v2/guide/list.html#Mutation-Methods

<template>
  <div id="app">
    <div v-for="item in array.sort((a, b) => a.value - b.value)" :key="item.name">
        {{item.name}}: {{item.value}}
    </div>
  </div>
</template>

<script>
export default {
  name: 'app',
  data: () => {
    return {
      array: [
        {
          name: 'a',
          value: 20,
        }, {
          name: 'b',
          value: 50,
        }, {
          name: 'c',
          value: 10
        }
      ]
    }
  },
}
</script>

一応表示はされますが、先述のwarningがでます。
スクリーンショット 2020-02-03 11.40.47.png

解決方法

arrayをslice()で新しい配列にコピーした上で、新しい配列を並び替えします。
こうすることでarray自身は変更されなくなります。
[...array].sort()等でも可

<template>
  <div id="app">
    <div v-for="item in array.slice().sort((a, b) => a.value - b.value)" :key="item.name">
        {{item.name}}: {{item.value}}
    </div>
  </div>
</template>

まとめ

JavaScriptの Array.prototype.sort() は元配列をin placeに変更するので注意。


  1. defaultで100回ループしたら停止します 

6
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
amuyikam

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
6
Help us understand the problem. What is going on with this article?