1

Learning React and seeing the below code I read that React auto binds for you so the thiss in 1 and 2 work out well. I was wondering what would the this in each one refer to if React didn't auto bind it for you? Wouldn't the 2 this refer tot he input element and thus not be able to get to the 1 this?

var ContactForm = React.createClass({
  propTypes: {
    // ...
  },

  onNameInput: function(e) {
    //1
    this.props.onChange(Object.assign({}, this.props.value, {name: e.target.value}))
  },

  render: function() {
    return React.createElement('form', {className: 'ContactForm'},
      React.createElement('input', {
        // ...
        //2
        onInput: this.onNameInput,
      })
      // ...
    )
  }
});
stackjlei
  • 9,485
  • 18
  • 65
  • 113

1 Answers1

1

You can try this out for yourself by seeing what this component will do when you extend React.Component instead of using createClass({}).

That said, this is really just a JS question. this points to different things depending on how its used. If auto-binding did not take place in the above example, then it would most likely refer to window, if you were running React in the browser. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this for more details.

Wouldn't the 2 this refer tot he input element and thus not be able to get to the 1 this?

this #2 is in the scope of its surrounding function, specifically the render function. It points to whatever the this scope of that function is, which in this case is the component instance. During the assignment onInput: this.onNameInput, you are dereferencing onNameInput from the component instance, so when you make that assignment, it may no longer be bound to that instance. If onNameInput was never bound, when it is called its this will be window.

Dmitry Minkovsky
  • 36,185
  • 26
  • 116
  • 160
  • So when I replaced the 2nd `this.onNameInput` with a `function(){console.log(this)}`, it logged the window. How does React then know to have `this` refer to the component instance to look for a method with `this.onNameInput` and at the window when it's just `this`? – stackjlei Apr 16 '17 at 02:36
  • `function(){console.log(this)}` is never bound to that React component instance, so `this` will always be `window` when that function is called. I stress, though, that this has nothing to do with React and is a general JS question. – Dmitry Minkovsky Apr 16 '17 at 02:42
  • Yeah, I would read more about `this`, and most importantly, play with it, until you are satisfied you understand how it behaves. It's a strange part of JS. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this – Dmitry Minkovsky Apr 16 '17 at 02:46
  • Is the reason that `function(){console.log(this)}` is never bound to that React component instance because it's a anonymous function? So even though `onNameInput` calls this anonymous function, the caller of the console.log is not `onNameInput`, but the anonymous function itself, so that's why `this` refers to window object? – stackjlei Apr 16 '17 at 02:46
  • I wouldn't say it's because it's anonymous. Rather, it is because `this` inside a function is bound to the global scope unless the function is invoked with `new` or called with `.call()` or `.apply()` or is the product of binding with `.bind()`. – Dmitry Minkovsky Apr 16 '17 at 02:49
  • Try `(function(){console.log(this)}).bind('a')()`, for example. Run that in your console. It's the same as `(function(){console.log(this)}).call('a')` and `(function(){console.log(this)}).apply('a', [])` – Dmitry Minkovsky Apr 16 '17 at 02:49
  • I thought if you have an object `var a = {f:function(){console.log(this)}}`, then if you call `a.f()` you will get back the object `a` not the global window even without binding because the caller of `f` is `a`. In the same sense, shouldn't I get back the input element for `this` if I replaced the 2nd `this.onNameInput` with a `function(){console.log(this)}`? – stackjlei Apr 16 '17 at 09:24
  • "In the same sense, shouldn't I get back the input element for this if I replaced the 2nd this.onNameInput with a function(){console.log(this)}?" — Nope, because `this` in that function is not scoped lexically (meaning set to the this value of the enclosing execution context) but, rather, its value depends on how the function is called. Since it's not called with `call` or `apply` or from an object, `this` will be global. – Dmitry Minkovsky Apr 16 '17 at 15:06
  • When you call a function directly from an object, as with `a.f()`, that function's home is that object. But once you assign that function to a variable, you can think of it as homeless. It's no longer bound to that object or any other object, unless you've explicitly permanently bound it, Before `Function.prototype.bind` was widely supported, people would implement it themselves in libraries like underscore and lodash and the implementation would look like `function bind(fn, target) { return function() { fn.apply(target, arguments) }; }`. That's really all "binding" is. – Dmitry Minkovsky Apr 16 '17 at 15:11
  • When you talk about "auto binding' in React, that means the base constructor for React components looks through all the functions on its prototype and replaces them with bound versions, so you can pass those functions around and not worry about permanently binding them yourself. – Dmitry Minkovsky Apr 16 '17 at 15:15