0

In this example I have an object that got a name as a string parameter and a function that just logs this

const me = reactive({
  name: "foo",
  sayHi: function () {
    console.log("Hi I am : ", this);
  },
});

In the template I instentiate another component that after 3000ms of it creation emits an event sayHi. This component is created twice :

  1. <Comp :name="me.name" @sayHi="me.sayHi"/>
  2. <Comp :name="me.name" @sayHi="me.sayHi()"/>

I kind of understand the diffrence between an event handler with and without parentheses explained here but I can't understand why the this in the first one is undefined but in the second one its the object itself as I expect.

walox
  • 567
  • 1
  • 7
  • 26

2 Answers2

2

When v-on directive is provided with @sayHi="me.sayHi()" inline expression, it's wrapped with a function and compiled to something like:

el.addEventListener('sayHi', $event => { vm.me.sayHi() })

When a value is a function value like @sayHi="me.sayHi", event handler is a function itself:

el.addEventListener('sayHi', vm.me.sayHi)

As explained in this question, this context is not parent object when its method is used as a callback, and this happens when it's passed as event handler. A function should be explicitly bound to the desired context to receive it as this.

The difference with this question is that sayHi is not component method. Component methods in Vue options API are internally bound to component instance, this doesn't apply to composition API.

sayHi should refer reactive value explicitly instead of using this:

const me = reactive({
  name: "foo",
  sayHi: () => {
    console.log("Hi I am : ", me.name);
  },
});

There's no benefit from using sayHi function as a method of reactive object, it's ignored by Vue reactivity API. Unless there are reasons for me to be an object, e.g. it's passed to other components, a state and function could be separated:

const me = ref("foo")
const sayHi = () => {
    console.log("Hi I am : ", me.value);
};
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 1
    I don't think any of this answers the question. He said he understood the difference between method and inline detection by the template compiler, and I'm pretty sure he knows that, in composition API, `this` does not refer to the component instance. His issue is different, and he has good reasons to call this method of a reactive object. Besides, in your first paragraph, the two cases you describe are identical. You forgot to add parenthesis to the first case: `@sayHi="me.sayHi()"`. – cyp-v Apr 05 '23 at 13:04
  • @cyp-v Thanks for noticing a typo. I wouldn't jump into conclusions regarding OP's awareness, but my point is that `this` is magically becomes component instance when a method is defined in `methods`, but in any other case, including composition api, normal JS behavior occurs and results in this problem https://stackoverflow.com/q/20279484 . It's unnecessary to follow OOP paradigm make sayHi a method of me, I don't see this often in composition code. But regardless of that, it's necessary to make sure it doesn't use wrong `this` when it's used as a callback, this isn't specific to Vue – Estus Flask Apr 05 '23 at 18:52
1

Event call compiles differently

// <Comp :name="me.name" @sayHi="me.sayHi"/>
let fn = me.sayHi
fn($event) // `this` is undefined us you just calling a function

and

// <Comp :name="me.name" @sayHi="me.sayHi()"/>
// or
// <Comp :name="me.name" @sayHi="() => me.sayHi()"/>
let fn = () => me.sayHi()
fn($event) // `this` is `me` as you are calling an object method
Dimava
  • 7,654
  • 1
  • 9
  • 24