1

I have run in to an issue where mounted will not run the function provided. I have just populated the array to illustrate it's structure. In practice it receives all it's data from the created instance which connects to the back end.

   new Vue({
el: '#app',
data() {
    return {
        products: [{
            "_id": "150",
            "name": "Milk",
            "description": "Skimmed",
            "price": "10",
            "ratings": [{
                    "email": "xyz@mail.com",
                    "rating": "5"
                },
                {
                    "email": "abc@mail.com",
                    "rating": "3"
                },
                {
                    "email": "def@mail.com",
                    "rating": "1"
                },
            ]
        }]
    }
},
created() {
    fetch('http://localhost:3000/api/products')
        .then(res => res.json())
        .then(json => this.products = json)
},


mounted() {
    // mapping each item of products to merge averageRating calculated
    this.products = this.products.map(product => {
        // ratings summation
        const totalRatings = product.ratings.reduce((acc, {
            rating
        }) => acc += Number(rating), 0)
        const averageRating = totalRatings / product.ratings.length
        // returning the merge of the current product with averageRating
        return {
            ...product,
            averageRating
        }
    })
}
})

created receives all the products without a problem. The issue is that the mounted instance should calculate the average of all product ratings and store it in this.products. I use name: {{product.name}}, averageRating: {{ product.averageRating }to display the product and it's ranking on HTML.

But this does not happen. It's almost as if it skips the mounted bit. Oddly this works just fine when I call it with <button @click.prevent="getAverage()">

methods : {

getAverage() {

this.products = this.products.map(product => {

    const totalRatings = product.ratings.reduce((acc, {
        rating
    }) => acc += Number(rating), 0)
    const averageRating = totalRatings / product.ratings.length

    return {
        ...product,
        averageRating
    }
    })
}
})

I don't know what could be wrong. There are no errors. I have tried chaining it to the .then() in the created instance. I need this function to run automatically without having to press a button. I have also tried using various other lifecycle hooks such as beforeMount.

I have also tried

mounted() {

    this.getAverage()

    }

Any help will be highly appreciated. Thanks in advance.

Calv
  • 125
  • 2
  • 11
  • 1
    Hi!, probably the time between created and mounted is less than the fetch, so, the mounted not working well – Ignacio Jan 22 '20 at 14:27
  • Possibly, how could I delay it? I tried setTimeout earlier but I don't think I implemented it correctly. I thought chaining it to `.then` would make it so that it would run after `this.products` is populated. Maybe I'm wrong. – Calv Jan 22 '20 at 14:30
  • 1
    Try do the fetch in the mounted instance and in the 'then' put the code of getAverage, i mean, do all your stuff in the mounted instance – Ignacio Jan 22 '20 at 14:39
  • Many thanks this works. – Calv Jan 22 '20 at 14:43

1 Answers1

1

The issue is that the mounted instance should calculate the average of all product ratings and store it in this.products

It shouldn't because remotely fetched data isn't ready yet at the moment when mounted runs. The same function works in click handler because it runs asynchronously when data is ready.

The problem is similar to this question and the solution is similar, too. It should be rewritten as (then is replaced with async..await syntactic sugar for readability):

async mounted() {
    const res = await fetch('http://localhost:3000/api/products');
    const json = await res.json();
    this.products = json;
    this.products = this.products.map(...)
}

It's incorrect to use created for asynchronous side effects, primarily because they aren't expected to complete at the end of this lifecycle hook, which is a common problem. This is very similar to React's componentWillMount and componentDidMount lifecycle hooks, the former was often misused for asynchronous side effects and therefore deprecated.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565