LoginSignup
26
33

More than 5 years have passed since last update.

Vue.js でのツリー構造など循環参照の具体的コピペ

Last updated at Posted at 2017-11-20

Vue.js での「この単純なアプリケーション」にて入れ子を実現する

子から親を参照可能に

コンポーネント間の循環参照の記述の通り、
子側から親を配下のコンポーネントとするための記述を行うためには
beforeCreate のタイミングで require を実施するか、
components への指定にて import を行わせる

  • Nuxt.js においては require ではうまくいかなかったので
    両方うまくいった components での import のほうがよさそうです
/components/TodoListItem.vue(子)
<script>
  export default {
    // Nuxt.js ではこちらのほうがうまくいった
    components: {
      TodoList: () => import("./TodoList.vue")
    },
    ・・・
    beforeCreate() {
      // 親となっている TodoList を template にて使用可能とする
      // - ただしこの記述は Nuxt.js ではうまくいかなかった
      //this.$options.components.TodoList = require('./TodoList.vue')
    }
    ・・・
  };
</script>

data から props への変更による循環参照の回避

子側から呼び出し可能となったところで変更を行わず template に記載すると
循環参照によりエラーが発生するのでプロパティの種類を変更する

/components/TodoList.vue(親)
  export default {
    ・・・
    props: {
      // あくまで親要素より受け取るという状態にすることで無限ループを回避
      todos: {
        type: Array,
        // 「一番上」の TodoList 用にデフォルト値を指定する
        default() {
          return [
            {
              id: nextTodoId++,
              text: 'Learn Vue'
            },
            {
              id: nextTodoId++,
              text: 'Learn about single-file components'
            },
            {
              id: nextTodoId++,
              text: 'Fall in love'
            }
          ];
        }
      }
    },
    data () {
      return {
        newTodoText: '',
        // TodoListItem より受け取れるよう独自のプロパティとしては持たせない
        //todos: [
        //    {
        //      id: nextTodoId++,
        //      text: 'Learn Vue'
        //    },
        //    ・・・
        //]
      }
    },
    ・・・
  };

入れ子を実装

子であった TodoListItem にて TodoList を呼び出し
その子となる TodoList は呼び出しもとよりデータを受ける

/components/TodoListItem.vue(入れ子)
<template>
  <li draggable="true">
    {{ todo.text }}
    <button @click="$emit('remove', todo.id)">
      X
    </button>
    <!-- 入れ子を実装しつつ「子要素」への props down -->
    <TodoList
      :todos="todos"
      />
  </li>
</template>

<script>
  export default {
    ・・・
    data() {
      // もとは親であった TodoList のプロパティを TodoListItem 自身に持たせる
      return {
        todos: []
      };
    }
    ・・・
  };
</script>

実例 (Warning 有)

・・・これだと削除時に「props」をいじるなと注意されてしまう

[Vue warn]:
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: "todos"

「prop の値を基に data を使いなさい」(おおまかに)

props down を行いつつ data も使用

props として配列を受け取り、そのまま使用していたことが問題を発生させていたので
props でいったん受けたものを data にて流用する形にする

/components/TodoList.vue
  ・・・
  props: {
    // ここでは todos を受け取らないように変更
    prop_todos: {
      type: Array,
      default() {
        return [
          {
            id: nextTodoId++,
            text: 'Learn Vue'
          },
          ・・・
        ];
      }
    }
  },
  data () {
    return {
      newTodoText: '',
      // props の値を data として使用
      todos: this.prop_todos
    }
  },
  ・・・
/components/TodoListItem.vue(todosからprop_todosへ)
<template>
  <li draggable="true">
    ・・・
    <!-- 渡す対象を prop_todos に変更 -->
    <TodoList
      :prop_todos="todos"
      />
    ・・・
  </li>
</template>

実例 (props を data にして Warning 対応)

props への操作で Warning が発生して前述の事例を対応したもの
https://codesandbox.io/s/5vkv67yw4

26
33
1

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
26
33