【Vue】エラーの発生原因と対処法:error The “xxx” property should be a constructor |vue/require-prop-type-constructor

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

Vueを使っている時に上記のようなエラーが発生したときの対処法についてまとめています。


エラーの内容

親コンポーネントからpropsプロパティとして受け取ったデータを、子コンポーネントの中でcomputedのgetとsetを使って変更しようとしたときに以下のようなエラーが発生。

error  The "xxx" property should be a constructor  vue/require-prop-type-constructor

指定したプロパティ(上記ではxxx)はコンストラクタでなければいけないという内容です。

コンストラクタとはVueインスタンスを生成するときに実行される関数のことです。


エラーの実例

例えば、以下のように親コンポーネント「HelloWorld.vue」から子コンポーネント「UserDetail.vue」に「xxx」というデータを渡したときに発生します。

親コンポーネント

<template>
  <div class="hello">
    <UserDetail 
     :xxx="xxx"
     @changeXxx="changeXxx"
    />
    <div>xxxの値:{{xxx}}</div>
  </div>
</template>

<script>
import UserDetail from './UserDetail'

export default {
  name: 'HelloWorld',
  components: {
    UserDetail
  },
  data(){
    return{
      xxx: "secret"
    }
  },
  methods:{
    changeXxx(newVal){
      this.xxx = newVal
    }
  }
}
</script>

<style scoped>
省略
</style>

子テンプレート「UserDetail.vue」に「:xxx=”xxx”」としてdataのxxxプロパティの値を渡しています。

「@changeXxx=”changeXxx”」は子テンプレートでcomputedのsetメソッドを使ってxxxの値を書き換えた場合に、発火させるイベントです。

合わせて読みたい

子テンプレートでpropsとして受け取ったデータを、子テンプレートの中で変更する方法については下記をご参考ください。

【Vue】propsのデータを変更する方法


子コンポーネント

<template>
  <div id="user-detail">
    <h1>ユーザーの詳細ページです</h1>
    <ul class="profile-list">
      <li>xxx: <input type="text" v-model="yyy"></li>
    </ul>
    <p></p>
  </div>
</template>

<script>
export default {
  props:{
    xxx: ""  
  },
  computed:{
    yyy:{
      get(){
        return this.xxx
      },
      set(newVal){
        this.$emit("changeXxx", newVal)
      }
    }
  }
}
</script>

<style scoped>
省略
</style>

子コンポーネントでは、input type="text" v-model="yyy"として、双方向データバインディングでyyyを変更できるようにしています。

このyyyは親コンポーネントから受け取ったpropsを表示・変更するためのプロパティです。

なぜ、このような処理をしているかについては、上記の「合わせて読みたい」をご参考ください。


原因

発生原因は、親コンポーネントから受け取ったデータを、子コンポーネントのpropsで定義するときにデフォルトの値を値として設定したことです。

例えば、親コンポーネントで定義しているデータ「xxx」のプロパティが文字列だった場合、子コンポーネントで以下のように値が存在しない場合のデフォルト値に、空文字や値を指定した場合に発生します。

  props:{
    xxx: ""    
  },
  props:{
    xxx: "AAA"    
  },

Vue.jsではpropsの値は1つしかありません。このため、子コンポーネントでpropsのプロパティの値を直接指定してしまうと、親コンポーネントで指定した値と被ることにより発生します。


対処法

property should be a constructorの対処法は3つあります。

対処法
  1. 型を指定する。{ type: 型 }
  2. 関数を使って記述する。
  3. undefinedを指定する。

型を指定するのが最も一般的だと思います。「関数を使って記述する」と「undefinedを指定する」でエラーは表示されなくなりますが、本来意図したい意味とは違うのかなと思います。


型を指定する

1つ目の方法はpropsで受け取ったデータの型を以下のように指定する方法です。

  props:{
    プロパティ名: { type: 型 }  
  },

型は親コンポーネントで指定しているデータに合わせてください。

実例

例えば文字列の場合は{type: String}とします。

  props:{
    xxx: {type: String} 
  },

これで、The “xxx” property should be a constructorのエラーは表示されなくなり、正常に動くようになります。


関数で記述する

2つ目の方法は関数で記述する方法です。

  props:{
    プロパティ名: () =>( 値 )  
  },

() => (値)は指定した値を生成する関数です。

注意点

指定した値が初期値になるわけではありません。例えば、親コンポーネントのプロパティの値が「””」のように空の文字列であっても、() => (値)で指定した値は表示されません。

ただ、エラーが出なくなるというだけです。


実例

例えば文字列の場合は() => ("")とします。

  props:{
    xxx: () => ("")
  },

これで、The “xxx” property should be a constructorのエラーは表示されなくなり、正常に動くようになります。


undefinedを指定する

3つ目の方法はpropsで受け取ったデータの値にundefinedを指定する方法です。

  props:{
    プロパティ名: undefined  
  },

型は親コンポーネントで指定しているデータに合わせてください。

実例

以下のようにプロパティの値をundefinedとします。

  props:{
    xxx: undefined
  },

これで、The “xxx” property should be a constructorのエラーは表示されなくなり、正常に動くようになります。


参考:実例用のコード

親コンポーネント

<template>
  <div class="hello">
    <UserDetail 
     :xxx="xxx"
     @changeXxx="changeXxx"
    />
    <div>xxxの値:{{xxx}}</div>
  </div>
</template>

<script>
import UserDetail from './UserDetail'

export default {
  name: 'HelloWorld',
  components: {
    UserDetail
  },
  data(){
    return{
      xxx: "secret"
    }
  },
  methods:{
    changeXxx(newVal){
      this.xxx = newVal
    }
  }
}
</script>

<style scoped>
省略
</style>

子テンプレート「UserDetail.vue」に「:xxx=”xxx”」としてdataのxxxプロパティの値を渡しています。

「@changeXxx=”changeXxx”」は子テンプレートでcomputedのsetメソッドを使ってxxxの値を書き換えた場合に、発火させるイベントです。

合わせて読みたい

子テンプレートでpropsとして受け取ったデータを、子テンプレートの中で変更する方法については下記をご参考ください。


子コンポーネント

<template>
  <div id="user-detail">
    <h1>ユーザーの詳細ページです</h1>
    <ul class="profile-list">
      <li>xxx: <input type="text" v-model="yyy"></li>
    </ul>
    <p></p>
  </div>
</template>

<script>
export default {
  props:{
    xxx: {type: String}
    // xxx: () => ("")
    // xxx: undefined
    // xxx: "" //エラー
  },
  computed:{
    yyy:{
      get(){
        return this.xxx
      },
      set(newVal){
        this.$emit("changeXxx", newVal)
      }
    }
  }
}
</script>

<style scoped>
省略
</style>

子コンポーネントのpropsの部分をコメントアウトで切り替えると試せます。

  props:{
    xxx: {type: String}
    // xxx: () => ("")
    // xxx: undefined
    // xxx: "" //エラー
  },


画面の表示

デフォルトでは以下のように表示されます。

子コンポーネントの中で表示している「secret」の文字を、「changed」に変えると、親コンポーネントの要素として下部に表示している「xxxの値」の部分も変更され、正しく動作します。

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