3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Vue で `You may have an infinite update loop` の警告が出る原因とその対処法

Last updated at Posted at 2020-07-24

概要

Vue を使っていて [Vue warn]: You may have an infinite update loop in a component render function. の警告が出た場合、書き方のどこかがおかしいので直す必要がある。

この記事では、原因とその対処法について書く。

まとめ

  • 上記の警告が出るのは、テンプレート(から呼ばれるメソッド)の中で data property の値を変更しているのが原因 (ただし v-on:xxx="..." の右辺で値を変更するのは何も問題ない)。

モデルケース

次のような要件の画面を作るとする。

  • テキストボックスの入力内容に応じてリアルタイムでバリデーションする
    • ユーザー名は最大 11 文字までとする
  • バリデーションエラーがあるときのみ、
    • テキストボックスの下にエラー表示する
    • テキストボックスに error という class をつけて枠を赤くする

※あくまでサンプルなので、 API 呼び出しはしない。

これを実現するための実装方法は1つではないが、
仮に以下のような Vue コンポーネントを定義すると、冒頭の infinite update loop 警告が出る。

警告が出る例
<template>
  <form>
    <div>
      ユーザー名:
      <input type="text" v-model="user_name" 
             v-bind:class="[ validateMaxLength('user_name', 11) ]"
      > 
      <p class="error" v-if="errors.user_name">{{ errors.user_name }}</p>
    </div>
    <!-- 以下、このような項目が複数並ぶ -->
  </form>
</template>

<script>
export default {
  data() {
    return {
      user_name: '',
      errors: {},
    };
  },
  methods: {
    /**
     * @param {string} name
     * @param {number} maxLength 最大文字数
     * @return {string} スタイルクラス名
     */
    validateMaxLength(name, maxLength) {
      const length = Number(this[name].length);
      let styleClass;
      if (length > maxLength) {
        this.$set(this.errors, name, String(maxLength) + '文字以下にしてください');
        styleClass = 'error';
      } else {
        this.$delete(this.errors, name);
        styleClass = '';
      }
      return styleClass;
    }
  }
};
</script>

<style>
input[type="text"].error {
  border-color: red;
}
p.error {
  color: red;
}
</style>

原因と解決方法

      <input type="text" v-model="user_name" 
             v-bind:class="[ validateMaxLength('user_name', 11) ]"> 

のように v-bind:class の中で validateMaxLength メソッドを呼び出しているのが原因。

validateMaxLength メソッドの中では this.$setthis.$delete を使って this.errors の状態を変化させている。
描画処理をしている途中で data property を変更すると無限ループになるおそれがあるため、 You may have an infinite update loop の警告が出る、ということ。

この警告が出ないようにするには、いくつか修正案がある。

修正案 1

this.errors の状態を変更する処理を v-on:xxx に移す。
v-bind:class="..." の中では this.errors読み取るだけにする。

      <input type="text" v-model="user_name" 
             v-bind:class="{ 'error': errors.user_name }" 
             v-on:input="validateMaxLength('user_name', 11)"
      >

修正案 2

this.errors の状態を変更する処理を watch に移す。
v-bind:class="..." の中では this.errors読み取るだけにする。
具体的には、

      <input type="text" v-model="user_name" 
             v-bind:class="{ 'error': errors.user_name }" 
      >

にした上で this.user_namewatch して validateMaxLength を実行すればよい。

watch: {
  user_name(newValue) {
    this.validateMaxLength('user_name', 11);
  }
},

なお、上記のいずれの修正案も validateMaxLength の戻り値を使っていないため、修正後はメソッドの戻り値が不要になる。
つまり以下でよいということ。

    /**
     * @param {string} name
     * @param {number} maxLength 最大文字数
     * @return {void}
     */
    validateMaxLength(name, maxLength) {
      const length = Number(this[name].length);
      if (length > maxLength) {
        this.$set(this.errors, name, String(maxLength) + '文字以下にしてください');
      } else {
        this.$delete(this.errors, name);
      }
    }
3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?