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`
predate ES6.
```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 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`.
To make this a little clearer, let's just use a different name.
```javascript
let bark2 = 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 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:
```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!".
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
----
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 ;
}
sayName() {
alert(this.props.name);
}
}
```
In React, you invoke like this: ``. 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:
```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 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 can we fix our component? We just do binding ourselves, like this:
```jsx
;
```
Or with ES6 syntax:
```jsx
;
```
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 ;
}
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!