52

I have a question relavent to this code: https://github.com/reactjs/redux/blob/master/examples/async/containers/App.js

specifically:

  constructor(props) {
    super(props)
    this.handleChange = this.handleChange.bind(this)
    this.handleRefreshClick = this.handleRefreshClick.bind(this)
  }

I guess its a 2 part question.

  1. Why do I need to set handle change as an instance of class this.handleChange =, can't I just use static functions for handleChange and call it directly with in the class onClick={handleRefreshClick}> ?
  2. I have no idea whats going on here: this.handleRefreshClick.bind(this)

Thanks

Saad
  • 26,316
  • 15
  • 48
  • 69
  • not exactly, I dont quite understand in context of a class, especially number 2 – Saad Jul 12 '16 at 16:15
  • 1
    It doesn't change the meaning of `bind`. You use `bind` to maintain the scope to the `this`. In the context of react this allows you to call things like `this.setState` etc. – ctrlplusb Jul 12 '16 at 16:16

7 Answers7

68

Answered in reverse order...

  1. this.handleRefreshClick.bind(something) returns a new function, in which references to this will refer to something. This is a way of saving the current value of this, which is in scope during the call to the constructor, so that it can be used later when the function is called.
  1. If your functions don't require access to the state of your component, then sure, you don't need to bind them.

The argument in favour of adding these lines to the constructor is so that the new bound functions are only created once per instance of the class. You could also use

onClick={this.handleRefreshClick.bind(this)}

or (ES6):

onClick={() => this.handleRefreshClick()}

but either of these methods will create a new function every time the component is re-rendered.

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • 3
    but doing .bind(this) the whole idea of a class is that it encapsulates 'this' right, so why do I need to encapsulate the scope in a specific function when the entire instance of a class should encapsulate the scope – Saad Jul 12 '16 at 16:22
  • 1
    It is not good to bind or use arrow function in Render as it leads to reallocation of the function at every render. The better approach is to bind in constructor or use arrow functions in class. https://medium.freecodecamp.org/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56 – Black Oct 04 '17 at 15:32
  • 2
    Yep, that's what it says in my answer :) – Tom Fenech Oct 04 '17 at 15:36
  • I believe this answer could be improved upon by clarifying that this is only relevant to `ReactJS` and in normal circumstances there's no need to bind functions to `this` (in regular JS classes) – pzaj Mar 19 '18 at 09:18
  • 1
    @user1970395 on the contrary, I wouldn' t say that there's much in this answer which _is_ React-specific. Any class methods used in event handlers will always need `this` to be bound, if they access properties on the instance. – Tom Fenech Mar 19 '18 at 09:30
  • @TomFenech Hello, im new to react and javascript in general, i read that if you add a function to a class it gets attached to it's prototype. So won't it be able to reference the function `this.handleClick` by looking up through the prototype chain in case of React classes ? I didn't understand the last point you explained to @user1970395, Are you saying since the event handler is a function itself, `this` in that function will refer to the event handler's `this`? – abhinavm93 Apr 02 '18 at 11:54
  • 3
    @abhinavm93 I'm saying that unless you explicitly bind `this` to a function, its value depends on the context in which it is called. React uses the render function to create DOM elements with event handlers. From the context in which these events are handled, there is no knowledge of the class that these event handlers belong to, unless you bind it. – Tom Fenech Apr 02 '18 at 13:03
8

The reason why it's being done, is to bind the this keyword to that object. Like Tom said, calling a function from a class doesn't mean it's being called with the context of the object that created that function.

I think you might be getting confused because in the React examples/tutorials, using React.createClass() DOES bind this automatically for you. So you might be wondering why React.createClass() does it, but doesn't with ES6 class syntax.

This is because React didn't want to mess with ES6 specifications (binding this to functions from its class is not in the ES6 class spec), but at the same time, wanted to give its users the convenience of ES6 class syntax. You can read more about this below.

Github issue

Hopefully that sheds some light on why that happens.

julianljk
  • 1,297
  • 10
  • 12
5

this depends how the function is called, not how/where it is created.

When you look at the code, you see two "this", why? Seems weird, right? The thing is it is not about how it seems. It is about how it is called.

You are basically saying. Hey, when somebody calls you remember this means this class. not something else.

When somebody calls your class like: x.yourClass().bind(this) you are saying this is not x but the class itself(with props and states etc.).

Quick note, even when you call directly the class like yourClass() you are actually calling window.yourClass() on browser, also that is why in this case the this is window.

serkan
  • 6,885
  • 4
  • 41
  • 49
4

These 2 functions handleChange and handleRefreshClick are passed down as props to other components ,

They are bind to this because when the child component will call these functions they will always execute with the APP context.

You can remove these functions from the class but still you need to bind this since you would be updating some parts of your APP

Piyush.kapoor
  • 6,715
  • 1
  • 21
  • 21
4

All the answers here are good, but for clarity regarding:

  1. I have no idea whats going on here: this.handleRefreshClick.bind(this)

I think an example is best in describing the difference in behaviour.

// Class where functions are explicitly bound to "this" specific object
var Bindings = class {
  constructor() {
    this.Firstname = "Joe"
    this.Surname = "Blow"
    this.PrettyPrint = this.PrettyPrint.bind(this)
    this.Print = this.Print.bind(this)
  }

  Print(inputStr) {
    console.log(inputStr)
    console.log(this)
  }

  PrettyPrint() {
    this.Print(`${this.Firstname} ${this.Surname}`)
  }
}

// Class where "this" context for each function is implicitly bound to 
//  the object the function is attached to / window / global
//  identical class, except for removing the calls to .bind(this)
var NoBindings = class {
  constructor() {
    this.Firstname = "Joe"
    this.Surname = "Blow"
  }

  Print(inputStr) {
    console.log(inputStr)
    console.log(this)
  }

  PrettyPrint() {
    this.Print(`${this.Firstname} ${this.Surname}`)
  }
}

var bindings = new Bindings()
var noBindings = new NoBindings()

bindings.PrettyPrint() 
// > "Joe Blow"
// > Object { Firstname: "Joe", Surname: "Blow", PrettyPrint: PrettyPrint(), Print: Print() }

noBindings.PrettyPrint() 
// > "Joe Blow"
// > Object { Firstname: "Joe", Surname: "Blow" }

// noBindings has both functions & everything works as we expect, 
// if this is all you're doing, then there's practically little difference,
// but if we separate them from the original "this" context...

var b = { PPrint: bindings.PrettyPrint }
var nb = { PPrint: noBindings.PrettyPrint }

b.PPrint()
// > "Joe Blow"
// > Object { Firstname: "Joe", Surname: "Blow", PrettyPrint: PrettyPrint(), Print: Print() }
// PPrint calls "PrettyPrint" where "this" references the original "bindings" variable
// "bindings" has a function called "Print" which "PrettyPrint" calls

try {
  nb.PPrint() 
} catch (e) {
  console.error(e);
}
// > TypeError: this.Print is not a function
// PPrint calls "PrettyPrint" where "this" references the new "nb" variable 
// due to different "this" context, "nb" does not have a function called "Print", so it fails

// We can verify this by modifying "bindings" and seeing that it's reflected in "b"
bindings.Surname = "Schmo"
b.PPrint()
// > "Joe Schmo"
// > Object { Firstname: "Joe", Surname: "Schmo", PrettyPrint: PrettyPrint(), Print: Print() }

// We can also add a "Print" method to "nb", and see that it's called by PrettyPrint
nb.Print = function(inputStr) { console.log(inputStr); console.log(this) }
nb.PPrint()
// > undefined undefined
// > Object { PPrint: PrettyPrint(), Print: Print(inputStr) }

// The reason we get "undefined undefined", 
//  is because "nb" doesn't have a Firstname or Surname field.
//  because, again, it's a different "this" context
Lovethenakedgun
  • 731
  • 6
  • 22
1

I personally bind functions in constructor so that their references don't change on each re-render.

This is especially important if you are passing functions to read-only children that you don't need to get updated when their props don't change. I use react-addons-pure-render-mixin for that.

Otherwise, on each parent's re-render, binding will happen, new function reference will get created and passed to children, which is going to think that props have changed.

Anton Kuzmin
  • 821
  • 1
  • 10
  • 26
0

If you want to dive deep into JS proxies and prototypes we can provide a class wrapper to automatically bind all methods in an instance and its prototype chain (it even supports super):

// intercept `new`
const bindThis = what => new Proxy(what, {
    construct(_class, args, constructor) {

        const obj = Reflect.construct(...arguments);

        if (_class.name !== constructor.name) {
            return obj; // the base class, skip it
        }

        const bindContext = _obj => {
            for (const [name, def] of Object.entries(Object.getOwnPropertyDescriptors(_obj))) {
                
                if (typeof def.value === 'function' && name !== 'constructor' && 
                // avoid overridding by base class methods
                !Object.hasOwn(obj, name)) {
                    // bind context for all the methods
                    def.value = def.value.bind(obj);
                    // make look like ordinary props (enumerable)
                    def.enumerable = true; 
                    Object.defineProperty(obj, name, def);
                }
            }
        };

        let context = obj;
        do {
            // skip Object.prototype for clearness
            Object.getPrototypeOf(context) && bindContext(context);
        } while (context = Object.getPrototypeOf(context));

        return obj;
    }
});

const TEST = bindThis(class TEST {
    box = null;
    info = {
        content: {
            blah: "blah"
        }
    };

    init() {
        this.box = window.document.querySelector(".settings");
        this.box.addEventListener("change", this.handler);
    }

    handler(e) {
        console.log("handler2 this: %o", this);
        console.log("handler2 info: %o", this.info.content);
    }
});

const CHILD = bindThis(class CHILD extends TEST {

    isChild = true;
    handler(e) {
        console.log("OVERRIDDEN");
        super.handler(e);
    }

});

let t = new TEST();
let c = new CHILD();

t.init();
c.init();
<select class="settings">
<option>-</option>
<option value="1">option 1</option>
</select>
Alexander Nenashev
  • 8,775
  • 2
  • 6
  • 17