Skip to content

Instantly share code, notes, and snippets.

@fongandrew
Last active January 14, 2024 16:02
Show Gist options
  • Save fongandrew/f28245920a41788e084d77877e65f22f to your computer and use it in GitHub Desktop.
Save fongandrew/f28245920a41788e084d77877e65f22f to your computer and use it in GitHub Desktop.

Revisions

  1. fongandrew revised this gist Jul 1, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion react-bind.md
    Original file line number Diff line number Diff line change
    @@ -69,7 +69,7 @@ have a `favoriteWord` property, so calling the function results in an exception.

    Before a Javascript expert shouts at me, note that in Javascript-land, we
    generally think of the relationship between `dog` and `bark` not as "parent"
    and "child", but that `dog` is "context" for `bark`. `dog.bark` means
    and "child", but that `dog` is the "context" for `bark`. `dog.bark` means
    "Call the bark function with the context of dog". And `this` always refers
    to the context in a given function.

  2. fongandrew revised this gist May 24, 2017. 1 changed file with 10 additions and 3 deletions.
    13 changes: 10 additions & 3 deletions react-bind.md
    Original file line number Diff line number Diff line change
    @@ -69,9 +69,9 @@ have a `favoriteWord` property, so calling the function results in an exception.

    Before a Javascript expert shouts at me, note that in Javascript-land, we
    generally think of the relationship between `dog` and `bark` not as "parent"
    and "child", but as "caller" and "callee". `dog.bark` means "Hey dog, call
    your 'bark' function!". And `this` always refers to the caller in the callee
    function.
    and "child", but that `dog` is "context" for `bark`. `dog.bark` means
    "Call the bark function with the context of dog". And `this` always refers
    to the context in a given function.

    To understand why someone (if not necessarily you or me) might think this is
    cool, consider the following:
    @@ -90,6 +90,13 @@ original `dog` object. So we can freely assign `bark` to `cat.meow`. And when
    we call `cat.meow`, the caller is `cat`, so `this.favoriteWord` refers to "Meow!"
    instead of "Woof!".

    As an aside, the "context" for a Javascript function or class method is distinct
    from ["context" in React](https://facebook.github.io/react/docs/context.html).
    As the React developers themselves indicate, if you're just getting started with
    React, ignore React's version of context. However, you do need to understand
    context in the Javascript sense insofar that you're using classes to represent
    React components and invoking stuff like `this.props` or `this.setState({ ... })`.

    Bind
    ----

  3. fongandrew revised this gist May 13, 2017. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions react-bind.md
    Original file line number Diff line number Diff line change
    @@ -186,18 +186,18 @@ points to our function, just fine and dandy.
    But what React is doing behind the scenes is assigning `this.sayName` to another variable.
    That is, it's just like this:

    ```
    ```javascript
    let onClick = this.sayName;
    onClick(); // Technically a click event is passed to onClick
    // but this doesn't matter for our purposes
    ```

    And just like our dog example, we can an error. Because `this` is undefined. This is
    And just like our dog example, we get an error. Because `this` is undefined. This is
    extra confusing because in previous versions of React, React would "autobind" the
    event handler for you, so it would work. But at some point, Facebook decided to
    stop doing that, so ... here we are.

    So how we fix our component? We just do the "autobind" ourselves, like this:
    So how can we fix our component? We just do binding ourselves, like this:

    ```jsx
    <button onClick={this.sayName.bind(this)}>Say My Name</button>;
  4. fongandrew revised this gist May 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion react-bind.md
    Original file line number Diff line number Diff line change
    @@ -58,7 +58,7 @@ The `bark` variable is distinct from `dog.bark`.
    To make this a little clearer, let's just use a different name.

    ```javascript
    let = dog.bark;
    let bark2 = dog.bark;
    bark2();
    ```

  5. fongandrew revised this gist May 13, 2017. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions react-bind.md
    Original file line number Diff line number Diff line change
    @@ -3,7 +3,7 @@ Start With This

    Before getting to React, it's helpful to know what `this` does generally in Javascript.
    Take the following snippet of code. It's written in ES6 but the principles for `this`
    have been around for a while.
    predate ES6.

    ```javascript
    class Dog {
    @@ -58,7 +58,7 @@ The `bark` variable is distinct from `dog.bark`.
    To make this a little clearer, let's just use a different name.

    ```javascript
    let bark = dog.bark;
    let = dog.bark;
    bark2();
    ```

  6. fongandrew revised this gist May 13, 2017. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion react-bind.md
    Original file line number Diff line number Diff line change
    @@ -51,7 +51,7 @@ bark() # => Woof!

    It's tempting to say `this` and `self` do the same things. They both refer
    to the parent of a function in this case. But in the case of Python, `self`
    is determined at the time the funciton is defined. In the case of Javascript,
    is determined at the time the function is defined. In the case of Javascript,
    `this` is determined at the time the function is called.

    The `bark` variable is distinct from `dog.bark`.
  7. fongandrew revised this gist May 13, 2017. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions react-bind.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    This
    ----
    Start With This
    ---------------

    Before getting to React, it's helpful to know what `this` does generally in Javascript.
    Take the following snippet of code. It's written in ES6 but the principles for `this`
  8. fongandrew created this gist May 13, 2017.
    237 changes: 237 additions & 0 deletions react-bind.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,237 @@
    This
    ----

    Before getting to React, it's helpful to know what `this` does generally in Javascript.
    Take the following snippet of code. It's written in ES6 but the principles for `this`
    have been around for a while.

    ```javascript
    class Dog {
    constructor() {
    this.favoriteWord = "Woof!";
    }

    bark() {
    return this.favoriteWord;
    }
    }

    let dog = new Dog();
    dog.bark(); // => Woof!
    ```

    Cool. That makes sense. But remember, functions are also objects in their own right
    in Javascript, so let's try this:

    ```javascript
    let bark = dog.bark;
    bark(); // => Error
    ```

    Ruh roh. What happened? You probably got a message saying `favoriteWord` wasn't
    a property of `undefined`, or something similar. But `this.favoriteWord` clearly
    refers to the `Dog` instance, so what gives?

    The answer is that `this` is determined at the time the function is called,
    not the time the function is defined. This can be super confusing if you're
    coming from, say, Python, where the following code works:

    ```python
    class Dog:
    def __init__(self):
    self.favorite_word = "Woof!"

    def bark(self):
    return self.favorite_word;

    dog = Dog()
    bark = dog.bark
    bark() # => Woof!
    ```

    It's tempting to say `this` and `self` do the same things. They both refer
    to the parent of a function in this case. But in the case of Python, `self`
    is determined at the time the funciton is defined. In the case of Javascript,
    `this` is determined at the time the function is called.

    The `bark` variable is distinct from `dog.bark`.
    To make this a little clearer, let's just use a different name.

    ```javascript
    let bark = dog.bark;
    bark2();
    ```

    We're creating a new variable (`bark2`). `bark2` doesn't have a parent at the
    time it's called, unlike `dog.bark` (which has `dog` as a parent). `bark2`
    is a top level variable, so it's "parent" is `undefined`. `undefined` doesn't
    have a `favoriteWord` property, so calling the function results in an exception.

    Before a Javascript expert shouts at me, note that in Javascript-land, we
    generally think of the relationship between `dog` and `bark` not as "parent"
    and "child", but as "caller" and "callee". `dog.bark` means "Hey dog, call
    your 'bark' function!". And `this` always refers to the caller in the callee
    function.

    To understand why someone (if not necessarily you or me) might think this is
    cool, consider the following:

    ```javascript
    let cat = {
    favoriteWord: "Meow!"
    };
    cat.meow = bark;
    cat.meow(); // => "Meow!"
    bark(); // Nope, still broken
    ```

    This works because the `this` variable in the `bark` function isn't tied to the
    original `dog` object. So we can freely assign `bark` to `cat.meow`. And when
    we call `cat.meow`, the caller is `cat`, so `this.favoriteWord` refers to "Meow!"
    instead of "Woof!".

    Bind
    ----

    OK, let's add a new wrinkle. Consider this now:

    ```javascript
    let alwaysWoof = bark.bind(dog);
    alwaysWoof(); // => "Woof!"
    ```

    Why does this work? It's because calling `bind` on a function returns a copy of
    that function in which `this` is always set to whatever arg you pass to `bind`.
    This applies even if we change the caller of the bound function:

    ```javascript
    cat.meow = alwaysWoof;
    cat.meow(); // => "Woof!"
    ```

    In the class context, it's pretty common to bind to `this`:

    ```javascript
    class ConsistentDog {
    constructor() {
    this.favoriteWord = "Woof!";

    let bark = function() {
    return this.favoriteWord;
    }
    this.bark = bark.bind(this);
    }
    }

    let conDog = new ConsistentDog();
    let conBark = conDog.bark;
    conBark(); // => "Woof!"
    ```

    Writing `.bind(this)` over and over is pretty annoying, so in ES6,
    you can also avoid writing `.bind(this)` with the `() => ...`
    syntax:

    ```javascript
    class SimpleDog {
    constructor() {
    this.favoriteWord = "Woof!";
    this.bark = () => this.favoriteWord;

    /*
    Or you can do this if you need more than one statement
    for your function.
    this.bark = () => {
    let simpleWord = this.simpleWord;
    return simpleWord;
    };
    */
    }
    }

    let simDog = new SimpleDog();
    let simBark = simDog.bark;
    simBark(); // => "Woof!"
    ```

    Now with React Classes
    -----------------------

    Still with us? OK, now to bring in React. Consider this React component,
    defined as an ES6 class:

    ```jsx
    class Welcome extends React.Component {
    render() {
    return <button onClick={this.sayName}>Say My Name</button>;
    }

    sayName() {
    alert(this.props.name);
    }
    }
    ```

    In React, you invoke like this: `<Welcome name="Bob" />`. This renders a button.
    Clicking the button should trigger an alert with "Bob".

    Except it doesn't. Because in the above example, `this` would be undefined in the
    `sayName` function.

    What's happening inside the render function is that `this` refers to the current instance
    of our React component. That component has a `sayName` function defined, so `this.sayName`
    points to our function, just fine and dandy.

    But what React is doing behind the scenes is assigning `this.sayName` to another variable.
    That is, it's just like this:

    ```
    let onClick = this.sayName;
    onClick(); // Technically a click event is passed to onClick
    // but this doesn't matter for our purposes
    ```

    And just like our dog example, we can an error. Because `this` is undefined. This is
    extra confusing because in previous versions of React, React would "autobind" the
    event handler for you, so it would work. But at some point, Facebook decided to
    stop doing that, so ... here we are.

    So how we fix our component? We just do the "autobind" ourselves, like this:

    ```jsx
    <button onClick={this.sayName.bind(this)}>Say My Name</button>;
    ```

    Or with ES6 syntax:

    ```jsx
    <button onClick={() => this.sayName()}>Say My Name</button>;
    ```

    And it should work!

    One final note -- when we bind a function in React, we can do that not only when
    the render function is called, but before as well. So take this:

    ```jsx
    class Welcome extends React.Component {
    constructor(props) {
    super(props);
    this.boundSayName = this.sayName.bind(this);
    }

    render() {
    return <button onClick={this.boundSayName}>Say My Name</button>;
    }

    sayName() {
    alert(this.props.name);
    }
    }
    ```

    We can do `this.boundSayName` instead of `this.boundSayName.bind(this)`. Because
    `this.boundSayName` was already bound to `this` in the constructor.

    And that's it! Hope it helps!