Can someone tell me why I get undefined
when trying to pass a prop down to child component?
Error: Cannot read properties of undefined (reading 'title')
Sandbox reproduction: https://codesandbox.io/s/little-silence-3lgd9?file=/components/PostEditor.vue
Parent component:
<template>
<div>
<PostEditor v-bind="post" />
</div>
</template>
<script>
export default {
name: "PostsEdit",
data() {
return {
post: {},
};
},
async created() {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts/1"
);
this.post = await response.json();
},
};
</script>
Child component:
<template>
<section>
{{ title }}
</section>
</template>
<script>
export default {
post: {
type: Object,
default: () => {},
required: false,
},
data() {
return {
title: this.post.title,
};
},
methods: {
save() {
this.$emit("save", {
...this.formValues,
});
},
},
};
</script>
CodePudding user response:
v-bind="post"
The child prop is named "post", but the parent is trying to bind several properties with v-bind="post"
.
The binding name should match the target prop in the child:
<!-- <PostEditor v-bind="post" /> --> ❌ binds subproperties of post
<PostEditor v-bind:post="post" /> ✅ binds post
<PostEditor :post="post" /> ✅ binds post (shorthand)
post
default prop value
The child's post
prop has a default
option of () => {}
, but that arrow function returns nothing (undefined
), as the curly brackets start a block scope. This is effectively the same as not having the default
option.
You likely meant for default
to return an empty object, which requires wrapping the curly braces in parentheses:
// default: () => {} // ❌ returns undefined
default: () => ({}) // ✅ returns {}
data()
not reactive
Even with the prop default fixed above, the formValues.title
property in data()
is initialized to this.post.title
, which would be undefined
because post
is initially an empty object. The post
value passed in from the parent is updated asynchronously, so the value is still the initial value from the parent (also an empty object) in data()
.
Note that data()
is not reactive, so it's only called once at initialization. Changes to this.post
will not automatically update the data property.
Solution: Render child after post
populated
One solution is to defer rendering the child component until the post
prop is populated in the parent so that the post
prop would have the expected values for the child's data()
initialization.
In the parent, initialize post
to null
, and use v-if="post"
to conditionally render the child:
<template>
<div>