0
const Event = () => {

    // useEffect(()=>{
    //     document.getElementById('btn').addEventListener('click', function() { 
    //         console.log(myObj.showThis())
    //     })
    // })
   
    const myObj = {
        name: 'romeo',
        showThis: function() {
            console.log(this)
        }
    }

    return (
        <div>
            <button id='btn' onClick={myObj.showThis} >Fire Event</button>
        </div>
    )
}

In here, I am adding inline event handler which invokes the myObj.showThis method, and when it does, the console logs out undefined, but if I instead use the useEffect() hook to add event listener to the button, which invokes the same myObj.showThis function, now the console logs out the caller, which is the button, so this refers to the caller, but why is this undefined when I invoke the same method via inline event handler?

happy_story
  • 1
  • 1
  • 7
  • 17
  • Try `onClick={() => myObj.showThis()}` – Joel Hager Sep 05 '21 at 15:39
  • @JoelHager Well this does work, but .. `this` now points to the object owner, not the caller. How come? And also, why isn't my case working, with just the reference? – happy_story Sep 05 '21 at 17:01
  • @IloveCoffee `this` is set when the function is called, not when it is created. It will be set to the receiver of a method call, eg. `foo.bar()` `this` in `bar()` will be set to `foo`. When you pass a function reference it is often called without receiver. Assuming button was a non-native component it would receive your function as `function button({ onClick })` and would then call `onClick(event)` if you click the button. As you can see it is called without receiver, so `this` is set to the global object (`window`) or `undefined` in strict mode. – 3limin4t0r Sep 05 '21 at 18:35
  • Another way of solving your issue would be to avoid using `this` eg. use `console.log(myObj)` instead of `console.log(this)`. Or by binding `this` beforehand. `onClick={myObj.showThis.bind(myObj)}` I suggest checking out the [MDN `this`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) documentation for a better understanding. – 3limin4t0r Sep 05 '21 at 18:36
  • @3limin4t0r I don't understand. I'm new to React, so can you dumb it down? And please don't recommend I read about `this,` because I know exactly how `this` works, which is why I am perplexed at why is it not working in React the way it would work in vanilla JS. So, you said that if I pass a function reference to the inline button in the above example `onClick={refernece}`, now the function is invoked without a receiver.. what is a `receiver`? and how is this different from using `useEffect()` and adding an event listener to the button with the same function reference? – happy_story Sep 05 '21 at 19:46
  • @3limin4t0r What is this - `function button({ onClick })`? What are you referring to here? Please explain in relation to my actual above provided example, not abstract examples. – happy_story Sep 05 '21 at 19:50
  • @IloveCoffee https://gist.github.com/3limin4t0r/fd9f33c1b116abc851c09fa410df8ca8 – 3limin4t0r Sep 05 '21 at 21:02
  • @3limin4t0r Overall good explanation, and I appreciate your help, but there's just one tiny part missing from it, and that was the main thing that confused me, which is .. what about addEventListener? Why is it that when I use addEventListener and pass a reference to a regular function which logs out `this`, now `this` refers to the caller of the function, which in my case is the button, since the button is invoking the function, which makes `this` bind to the caller. I thought using inline event handler, `onClick={}` means the function reference is invoked by the button. Why is that not true? – happy_story Sep 06 '21 at 11:58
  • @3limin4t0r Is it because with addEventListener, the function reference is invoked by the listener, which is the button, where as with inline event handlers, like `onclick="function()"` you are not passing a reference, but just invoking the function inside the braces? and in React, the function reference that you pass, gets assigned to internal variable, which is then invoked without a receiver? by the way, I've always called 'receivers' just 'object owners'. – happy_story Sep 06 '21 at 12:15
  • @IloveCoffee The reason `btn.addEventListener("click", myObj.logThis)` logs the ` – 3limin4t0r Sep 06 '21 at 12:35
  • @3limin4t0r Okay- I think I understand now. Do you mind me asking you one more question? It's about class component, instead of functional component. I am looking at w3school's this example: `https://www.w3schools.com/react/showreact.asp?filename=demo2_react_events_this2` and I can't figure out how exactly is `this` binding to the component? I am talking about this: `this.shoot = this.shoot.bind(this)` they say I have to do this to bind `this` inside the later defined `shoot() {}` function to the component itself. But how is that happening? What is `this.shoot` equals to? – happy_story Sep 06 '21 at 14:13
  • How is `this.shoot.bind(this)` binding the component to `this`? Since `this` refers to the component, when you do `this.shoot.bind(this)`, aren't you binding the component to `shoot` , which is not even defined yet? This is confusing so much. Can you try to clarify this? – happy_story Sep 06 '21 at 14:14
  • Is it that `this.shoot.bind(this)` this is the below defined function whose `this` you bind to the component, and then you assign that function to a variable called `shoot` again which you then pass to the button below? I think that's what's happening, right? They've explained this in the most confusing way possible. – happy_story Sep 06 '21 at 14:17
  • What about the arrow function in here: `https://www.w3schools.com/react/showreact.asp?filename=demo2_react_events_this` why is `this` getting bound to the component, and not the global object? Since arrow functions don't bind `this` by default, and `this` inside arrow function always refers to the upper scale object, which in most case is the global object, so why in this case does `this` inside the arrow function refer to the component? When you pass the func reference to the button, it gets assigned to internal variable which is invoked.. so shouldn't `this` refer to the global object? – happy_story Sep 06 '21 at 14:24
  • I think I already figured it out. It's because class and constructors functions by default bind `this` to the new instance, so `this` inside the arrow function refers to one scale up, which is the class, which binds it to the instance, or the class itself. But I thought react components do not automatically bind `this`, so why does it get bound here? – happy_story Sep 06 '21 at 16:01
  • @IloveCoffee The reason `this` is bound to the component is because [arrow functions don't have their own `this` binding](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), and since you are setting a [public instance field](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields#public_instance_fields) `this` will refer to the instance that is being initialized (similar to `this` in the constructor). Here is another way of writing it. https://gist.github.com/3limin4t0r/1272330d21a5a95587e5b272e4ceea49 – 3limin4t0r Sep 09 '21 at 08:51

1 Answers1

0

You need to understand how 'this' works. it references different value depending on the context it is called. this may return different value for myObj.showThis() even inside the useEffect. This is not a factor of the useEffect rather For example

 useEffect(()=>{
      console.log(myObj.showThis()) // undefined because this is undefined in that context
     document.getElementById('btn').addEventListener('click', function() { 
       // here `this` references the actual object because it defined here as myObj
       console.log(myObj.showThis())
     })
})

and this is undefined because reactES6 does not auto bind this for function or methods

abubakri olaitan
  • 220
  • 3
  • 11
  • You literally did not answer any of my questions. My question is WHY is `this` undefined when invoked by the inline event handler when it should be referring to the caller, as it does in vanilla JS. I also don't understand why this `console.log(myObj.showThis())` invoked in `useEffect` returns undefined. – happy_story Sep 05 '21 at 17:15
  • I actually answered you. this is undefined because it looks at the context it was called not the caller. and ReactES6 does not auto bind this and that is why it is undefined. myObj.showThis is bound to nothing here. – abubakri olaitan Sep 05 '21 at 17:32
  • `this` refers to the caller when inside a regular function, as is the case with the `addeventlistener`, so why when I invoke the function from inline handler, it doesn't. That's the question. If it's undefined because react doesn't bind `this`, then why does `this` refer to the caller when invoked by the event listener when defined with `useEffect()`. – happy_story Sep 05 '21 at 18:05
  • It doesn't refer to the caller inside a regular function. It refers to the context where it is called. the context, may be the value of this at the call site, or where it enforced, or the actual object. in fact if you use ES6 arrow function for showThis. it will return undefined even inside the useEffect. Please refer to https://github.com/getify/You-Dont-Know-JS . It will give you more context – abubakri olaitan Sep 05 '21 at 18:54
  • It does not refer to the context, the context determines who does it refer to, and in the context in which it's in regular function, it refers to the caller -> https://jsfiddle.net/kgbjo93L/ – happy_story Sep 05 '21 at 19:39