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の値を書き換えた場合に、発火させるイベントです。
子コンポーネント
<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つあります。
型を指定するのが最も一般的だと思います。「関数を使って記述する」と「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の値を書き換えた場合に、発火させるイベントです。
子コンポーネント
<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の値」の部分も変更され、正しく動作します。