0
votes

I am getting the following error sporadically:

vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in render: "TypeError: Cannot read property 'books' of undefined"

found in

---> at src/views/Home.vue at src/App.vue warn @ vue.runtime.esm.js?2b0e:619 logError @ vue.runtime.esm.js?2b0e:1884 globalHandleError @ vue.runtime.esm.js?2b0e:1879 handleError @ vue.runtime.esm.js?2b0e:1839 Vue._render @ vue.runtime.esm.js?2b0e:3544 updateComponent @ vue.runtime.esm.js?2b0e:4060 get @ vue.runtime.esm.js?2b0e:4473 run @ vue.runtime.esm.js?2b0e:4548 flushSchedulerQueue @ vue.runtime.esm.js?2b0e:4304 (anonymous) @ vue.runtime.esm.js?2b0e:1980 flushCallbacks @ vue.runtime.esm.js?2b0e:1906 Promise.then (async) ...

in an app using Vue Apollo.

The view which contains the error is Home.vue, given below:

<template>
    <div class="home">
        <ApolloQuery :query="categoriesQuery">
            <template slot-scope="{ result: { data, loading }, isLoading }">
                <div v-if="isLoading">Loading...</div>
                <div v-else>
                    <a href="#" @click.prevent="selectCategory('all')" class="link-margin">All</a>
                    <a href="#" @click.prevent="selectCategory('featured')" class="link-margin">Featured</a>
                    <a href="#" v-for="category of data.categories" @click.prevent="selectCategory(category.id)" class="link-margin">
                        {{ category.id }}. {{ category.name }}
                    </a>
                </div>
            </template>
        </ApolloQuery>

        <div>
            <ApolloQuery :query="query" v-if="selectedCategory === 'all'">
                <template slot-scope="{ result: { data, loading }, isLoading }">
                    <div v-if="isLoading">Loading...</div>
                    <div v-else>
                        <div v-for="book of data.books" :key="book.id">
                            {{ book.id }}. {{ book.title }}
                        </div>
                    </div>
                </template>
            </ApolloQuery>

            <ApolloQuery :query="query" :variables="{ featured: true }" v-else-if="selectedCategory === 'featured'">
                <template slot-scope="{ result: { data, loading }, isLoading }">
                    <div v-if="isLoading">Loading...</div>
                    <div v-else>
                        <div v-for="book of data.booksByFeatured" :key="book.id">
                            {{ book.id }}. {{ book.title }}
                        </div>
                    </div>
                </template>
            </ApolloQuery>

            <ApolloQuery :query="query" :variables="{ id: selectedCategory }" v-else>
        <template slot-scope="{ result: { data, loading }, isLoading }">
          <div v-if="isLoading">Loading...</div>
          <div v-else>
            <div v-for="book of data.category.books" :key="book.id">
              {{ book.id }}. {{ book.title }}
            </div>
          </div>
        </template>
      </ApolloQuery>
        </div>
    </div>
</template>

<script>
// @ is an alias to /src
import categoriesQuery from '@/graphql/queries/Categories.gql'
import categoryQuery from '@/graphql/queries/Category.gql'
import booksQuery from '@/graphql/queries/Books.gql'
import booksFeaturedQuery from '@/graphql/queries/BooksFeatured.gql'


export default {
    name: 'home',
    components: {
    },
    data() {
        return {
            categoriesQuery,
            categoryQuery,
            booksQuery,
            booksFeaturedQuery,
            selectedCategory: 'all',
            query: booksQuery,
            categories: []
        }
    },
    methods: {
        selectCategory(category) {
            this.selectedCategory = category

            if (category === 'all') {
                this.query = booksQuery
            } else if (category === 'featured') {
                this.query = booksFeaturedQuery
            } else {
                this.query = categoryQuery
            }
        }
    },
}
</script>

<style>
    .link-margin {
        margin-right: 24px;
    }
</style>

So, if you start off with the default (i.e. "All") and you click on "Featured", then it will show the featured books. If, after clicking on "Featured", you then try to click on any of the other categories, the view will not update but the data can be seen in the Vue dev tools. There is no error in the console window.

Refreshing and starting off with the default, then clicking on any of the categories, will filter and show the books in that category. If, however, you click on a category and then try clicking on "Featured", you'll get the error message and a perpetual "Loading..." message.

That's the behavior I got when I tried it multiple ways.

I've tried remove the "Featured" portion, I have tried changing the logic around, all to no avail. The queries don't seem to be the issue.

I also asked this question on Laracasts, but haven't received a response yet, so thought I'd post here.

GitHub links:

Front end: https://github.com/matthew-spire/booksql-vue Back end: https://github.com/matthew-spire/booksql-laravel

1
How about gathering some more information with a tool like Sentry? It will log the entire stacktrace.online Thomas
I will look into Sentry. I have never heard of it before.user3727648
It seems error come from lost of reactivity. Did you try to assign blank object in your queries vars? Something like return {categoriesQuery: {}, categoryQuery :{}, …. Also did you try to set query with $set? vuejs.org/v2/api/#vm-setManUtopiK
Have you considered doing v-else-if sections for v-else-if="error" and v-else-if="data"? It's possible that an error is occurring or that your data is, for whatever reason, coming back as null or as an empty object. Ruling out possibilities will help narrow down the exact point of failure, which will in turn help determine the root cause.B. Fleming

1 Answers

0
votes

This question was answered by Austio here.

His answer:

In this scenario you are calling some property on something that is undefined. You can use lodash/get to prevent some of this. On our end we have created our own ApolloQuery component where we pass lodash get up as a slot prop.

<div v-for="book of data.books" :key="book.id">
<div v-for="book of data.category.books" :key="book.id">

// become this assuming that _get is the accessible lodash/get function
<div v-for="book of _get(data, 'books', [])" :key="book.id">
<div v-for="book of _get(data, 'category.books', [])" :key="book.id">