Home > Net >  Why do I get Undefined error when trying to pass async API data as prop from parent to child compone
Why do I get Undefined error when trying to pass async API data as prop from parent to child compone

Time:10-26

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>                                        
  • Related