stevendesu
stevendesu

Reputation: 16791

Simple VueJS issue (setting $data in mounted)

Edit

The Component.vue provided was part of a larger web app so I ripped out the relevant code for this question. What I didn't notice was a VERY tiny change I made in ripping out the code that had a very big impact.

There's a difference between:

mounted() {
    // ....
}

and:

mounted: () => {
    // ....
}

Upon careful investigation this morning I found this mistake in my code and I've updated the question to reflect the actual code that was failing.

Question

I may just be tired, but before going to bed I wanted to ask for help here and see if someone can find my issue. I have a very simple Vue component that isn't working:

Component.vue:

<template>
    <div>
        <p v-for="item in items">{{ item.text }}</p>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                items: []
            };
        },
        mounted: () => {
            var _this = this;
            $.ajax("/items.json").done(result => {
                _this.items = result;
            });
        }
    };
</script>

items.json

[
    {"text": "ABC"},
    {"text": "XYZ"}
]

The paragraphs are never rendering. Upon inspection it looks like _this.items doesn't exist prior to setting it in the AJAX handler (I expect it to be an empty array) and _this.$data also doesn't exist

~Is the value of this different in the mounted method than elsewhere in Vue?~ Or did I make a simple mistake?

Writing the mounted function in this way (with the colon) causes the value of this to be different. Why is that?

Upvotes: 3

Views: 13595

Answers (2)

stevendesu
stevendesu

Reputation: 16791

Upon further research, I have learned of the subtle difference between normal functions and arrow functions. I previously thought the latter was just a short-hand, but it also does not have its own context.

The method mounted: () => {} utilizes an arrow function, and therefore

...does not have its own this, arguments, super, or new.target

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Here is a simple example demonstrating the difference which can be tested in the Chrome console

let testFunc = function() { console.log(this); }
testFunc(); // Window {...}
testFunc.bind("test")(); // String {"test"}
testFunc = () => { console.log(this); }
testFunc(); // Window {...}
testFunc.bind("test")(); // Window {...}

When utilizing an arrow function, it becomes impossible to bind a value to this. This means that in the Vue internals they are unable to bind the Vue instance to this.

The method mounted() { } is only utilizing the ES6 short-hand for objects, and not an arrow function (therefore it does have its own context and you can bind the this variable)

Upvotes: 8

MattYao
MattYao

Reputation: 2555

Mounting hooks are often the most-used hooks, for better or worse. They allow you to access your component immediately before and after the first render. They do not, however, run during server-side rendering.

Use if: You need to access or modify the DOM of your component immediately before or after the initial render.

Do not use if: You need to fetch some data for your component on initialization. Use created() (or created + activated for keep-alive components) for this instead, especially if you need that data during server-side rendering.

So try to change mounted() to created() and see if it works. Also, I suggest you look at axios to send request which is a better alternative to $.ajax(). Make sure you are requesting the json file from the right path.

Upvotes: 2

Related Questions