tinymarsracing
tinymarsracing

Reputation: 637

Binding in React: What does `this` refer to if I don't bind?

I had some problems to understand the whole this issue in React (or JS in general) and found this very helpful article:

https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56

However, there is one basic thing that I'm still not sure about.

Let's take the Approach 2 as an example:

// Approach 2: Bind in Render
class HelloWorld extends React.Component {
  constructor(props) {
    super(props);
    this.state = { message: 'Hi' };
  }

  logMessage() {
    // This works because of the bind in render below.
    console.log(this.state.message);
  }

  render() {
    return (
      <input type="button" value="Log" onClick={this.logMessage.bind(this)} />
    );
  }
}

Now, let's look at the wrong version of this code where we just do not do the binding that is required in order to refer to the right this (the HelloWorld component):

// Wrong "naive" version of the code
class HelloWorld extends React.Component {
  constructor(props) {
    super(props);
    this.state = { message: 'Hi' };
  }

  logMessage() {
    // This works because of the bind in render below.
    console.log(this.state.message);
  }

  render() {
    return (
      <input type="button" value="Log" onClick={this.logMessage} />
    );
  }
}

My question is very simple: In that wrong version, it is my understanding that the this in console.log(this.state.message) within the logMessage function does not refer to the HelloWorld class object anymore. What does it refer to instead? Thank you!

EDIT: Turns out my understanding is wrong. This is not the this that does not work anymore. It's "the other" this at onClick={this.logMessage}! The reason will be given in the answers below - just wanted to correct this right here in the question.

Upvotes: 1

Views: 1081

Answers (3)

Boopathi Rajaa
Boopathi Rajaa

Reputation: 4729

Whenever you call a function using - (), the function context - this is set automatically based on whatever goes before ().

For example,

let A = {
  foo() {
    console.log(this === A);
  }
};

when you call foo like this -

A.foo() // prints true

the context function foo receives depends on A.foo, here it is A. But when you call the SAME function using a different member - for example -

let B = {
  foo: A.foo
};
B.foo(); // prints false

Even though you're still calling the exact same function, the function/method foo receives a different context (here B) -

And sometimes, you can force the context of a function using one of these three things -

1. .bind

Docs: MDN

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

2. .call, .apply

Both of them call the function with a given this value.

Docs: MDN Function.prototype.call

Docs: MDN Function.prototype.apply

3. Arrow function

Docs: MDN Arrow functions

class

JavaScript class methods have the same concepts -

class A {
  foo() {
    console.log(this instanceof A);
  }
}
let a = new A();
a.foo(); // prints true

and when you run the experiment of assigning this function to a different method of a different object,

let b = {
  foo: a.foo
};
b.foo(); // prints false

Whenever you pass a function somewhere else, you're not passing the context along with it, but you're expecting some context inside your function by using this and

The source of the issue

In your example,

<input type="button" value="Log" onClick={this.logMessage} />

specifically,

onClick={this.logMessage}

which is analogous to the above example -

let b = {
  foo: a.foo
};

So, now, whoever calls your function using b.foo (here React) needs to know the context. So you have to set the context for the function before you pass it to onClick

What does it refer to instead if you don't bind?

In strict mode, the following happens -

Thus for a strict mode function, the specified this is not boxed into an object, and if unspecified, this will be undefined

Source: MDN under the section "Securing JavaScript"

And in all of the above examples with b.foo, if you assign it to a variable instead of object property,

let c = a.foo;
c(); // this will be undefined in foo

So, in your logMessage method, the value of this will be undefined.

Upvotes: 3

Timothy
Timothy

Reputation: 34

The default value of 'this' depends on which event you are binding to. With JavaScript, in general, DOM events, such as onClick or onChange have 'this' as a pointer to the DOM element that triggered the event. For instance, if you were to create a button and attach a click listener, and in that listener, use console.log(this) you will see the button is the value of this.

Note: React has 'this' set as undefined by default.

Event handlers for setTimeout and setInterval, will set 'this' as a pointer to the window object.

There is an inherit danger to using bind. Each time you bind to a method, JavaScript creates a new method. The new method is an anonymous method unless you set it's value to a property or variable:

let myBoundMethod = myMethod.bind(this);

If you need to detach an event from a DOM element, unless you have a reference to the bound method, you will not be able to remove the event listener from the event. This can be a source of memory leaks in your application. Always use your debugging tools to monitor memory usage. It is normal for it to go up and down, but it should have a relatively stable baseline.

DOM elements removed from the DOM will often garbage collect along with their event listeners, but if there is still a reference in memory to the element, it will not be garbage collected.

Upvotes: 1

Muhammad Danial Iqbal
Muhammad Danial Iqbal

Reputation: 1776

this always refers to your class' attributes and behaviors but you have to bind this to the functions in which you want to use those attributes and behaviors. If you do not bind class level this with a function so "this" in that function refers to the attributes within the context of that function only.

You can also bind this in component cycle's callbacks like componentWillMount, constructor or anywhere where this has the reference to the class.

Forexampe:

componentWillMount(){
this.anyCallBack = this.anyCallBack.bind(this);
}
anycallBack(){
//now this will refer to the class
console.log(this);
}

Upvotes: 1

Related Questions