— Javascript, React — 6 min read
When I first started learning React, I was always confused that for some reason
React didn't understand that I had to use this
everywhere. I had to bind
this
when I called a function, or used this.state
, this.props
,
this.handleClick
; you name it, you had to consider this
and what it meant
in the context of whatever you did.
At first, I thought this was because React was poorly designed. In reality, its
a function of the way that JavaScript implements classes. For React to diverge
would be both a ton of work, and remove flexibility from the framework. This my
attempt at explaining how I understand this
, but hopefully with the context
of how to use it within React.
I stand on the shoulders of giants in terms of my understanding of javascript,
scope, and this
. I highly recommend that in addition to playing with these
concepts yourselves in REPLs and your code base, explore the learning options
out there. I personally have learned a lot of this material on Frontend
Masters with a particular shoutout for Kyle
Simpson and Will
Sentance for making absolutely phenomenal courses
that were not only fun to watch and participate in but were immensely helpful
in solidifying foundation knowledge of javascript.
this
exist in the first place?Before you can under this
in React, you have to understand what this
is in
vanilla JavaScript
Javascript variables are static or "lexically" scoped. When javascript gets compiled, it bases what variables a block of code has access to based on where it is defined in the code. Lexical is another word for "written", so it matters where a variable is defined.
In a function, you don't just have access to variables in your functions local
scope, you also have access to variables up the "lexical scope chain". If you
define a function fn2
within a function fn1
, then fn2
will be able to use
any variable defined in it own local variable environment and anything in
fn1
's environment.
1function fn1() {2 var foo = 'foo';3 var bar = 'bar';4 function fn2() {5 var bar = 'bar';6 var bam = 'bam';7 }8}
In this contrived example, fn2
would be able to access all four variables at
run time, but fn1
would only be able to access foo
and bar
. fn2
resides
within fn1
so it can see variables up the chain, but fn1
can't look for
variables down the chain.
What if I don't know what the variable will have at compile time? Maybe you
want to write a method that will reference data on an object the method is
defined on. In this case you need to dynamically determine the scope at
execution time, and for that, you are going to need this
.
Even MDN doesn't provide a great definition for what this
is. It is hard to
boil down to a single defition, and there are always caveats. This is my best
attempt at defining what this
is...
this
in javascript will by default reference the object which invoked the function
That doesn't sound so hard, except when you consider that sometimes you can use
bind
to make it bound to a new function, you can use arrow functions that
won't define their own this context, you can use call
or apply
to change
how this
interacts with your code. Ultimately, it can be quite tricky to
figure out what this
actually is.
The simplest way to use this
is to reference some kind of property on an
object inside a of function
defined on that object. Lets consider the an
example Student named Brady...
1var Brady = {2 name: 'Brady',3 grades: ['A','B',...],4 changeName: function(newName) {5 this.name = newName;6 },7}
In this example, our changeName
property is a function that changes the name of the Student object to be a new name.
1console.log(Brady.name); //Brady2Brady.changeName("Bill");3console.log(Brady.name); //Bill
Great! I have a Brady Object
and I can change the name on the object. This
implementation means I need to manually create the all my students. Lets work
towards a function that can create students
JavaScript is a prototypal language. Being a prototypal language means a lot of things, but one of the major consequences is that JavaScript did not originally have an implementation of Classes or Inheritance. But you could make something that looked very much like a class using the objects and function. Lets see how we might make a Student.
1var studentFunctions = {2 changeName: function(newName) {3 this.name = newName;4 },5}67function studentCreator(name, grades) {8 var newStudent = Object.create(studentFunctions);9 newStudent.name = name;10 newStudent.grades = grades;11 return newStudent;12}1314var brady = studentCreator('Brady', ['A','B']);15var bill = studentCreator('Bill', []);
We have defined a function that "creates" students. It attaches the functions
we want on every student to a new Object
with Object.create
. It then
attaches the properties that will define that particular student from the
arguments to the creator function, and then returns the newly created student.
You probably won't see classes written like this in JavaScript. This is
definitely a lot of code, but what is happening is obvious. When you call
studentCreator
, you are making a new object with the properties of
studentFunctions
, assigning the inputs to properties on that object, and then
returning it. But developers are by nature lazy, so they wanted to automate
this. To streamline this process, you can use the new
keyword. new
will do
4 things.
Object
Object
's prototype and the prototype of the function that called itObject
into the function as the this
contextthis
from the functionThat lets you take our above code and make it into something that looks like this...
1function Student(name, grades) {2 this.name = name;3 this.grades = grades;4}56Student.prototype.changeName = function(newName) {7 this.name = newName;8}910var brady = new Student('Brady',['A','B'])11var bill = new Student('Bill', [])
This is fewer lines of code and is much more declaritive. But if you don't understand whats happening under the hood, then the behavior you get might feel odd.
This is how javascript creates classes, which are defined by properties and
methods on an Object
. When you are using this
inside of these classes, you
are ultimately trying to reference the this
that is that Object
. You want to
get to the variable state
you put on the Object
, you have to get it with this.state
.
You want to access the handleChange
you defined as a property as
this.handleChange
. And most importantly inside those functions you need
this
to reference that class Object
so that you can do all the wonderful
things you want to do with your React Components
In Javascript, class
is just syntactic sugar for using the exact same
notation that I specified above. You could also write a student class like
this.
1class Student {2 constructor(name, grades) {3 this.name = name;4 this.grades = grades;5 }67 changeName(newName) {8 this.name = newName;9 }10}
classes have a couple extra bits (like the super
keyword) but for most cases,
they are the exact same thing. This syntax is easier to write, and has a ton of
advantages. But its largest disadvantage is that its very easy to create a
class in javascript and have no idea how it works under the hood, which can lead
to misunderstanding and bugs down the line.
To understand this
in React, you have to understand this
in plain old
javascript.
A long time ago, React added functionality to use the native ES6 class to make a React component. For better or for worse they chose to not auto-bind this, and instead implemented classes the same as ES6 would.
So largely, it is on you to bind the right this context to your functions so
that they can access the this
that has all the properties like setState
,
state
, and anything else you define on the class Object
. If you don't, then
when you go update the state, or change anything on this
, your function will
fail as this
will be undefined
.
this
to your functionsOne of the things that I don't like about React is just how many different
ways there are to bind this
. I think that it leads to confusion about what is
happening and where the this
binding is actually occuring. I'm going to try
and detail all the ways that I know how to bind this, and hopefully that should
give you a good idea of how you might see it happening.
But just as important as examples are understanding the tools that are used
to bind this
, and that means that we need to talk about regular functions,
arrow functions and the bind
keyword...
ES6 made a new syntax for functions that looks like this.
1var printHey = () => console.log('hey')
Its called an arrow function. In this case, that arrow function is equivalent to this function expression.
1var printHey = function() {2 return console.log('hey')3}
So whats the difference? That can be illustrated with another object.
1this.name = "Bill"23myCoolObject = {4 name: "Brady",5 printArrow: () => {6 console.log(this.name)7 },8 printReg() {9 console.log(this.name)10 }11}1213myCoolObject.printArrow() //Bill14myCoolObject.printReg() //Brady
It looks like these two functions do the same thing, but they actually print different things to the console. Why?
Arrow functions don't define their own this
context, they inherit it from the
lexical scope. Function declarations bind their this
context from the object
that they are defined on. So printReg
gets this.name
from myCoolObject
,
but printArrow
gets this.name
from the environmnet where myCoolObject
is
defined.
If a function doesn't use this
, then there is no difference between arrow and
function declarations. But this subtle difference allows us to use arrows to
define functions in React without binding then like normal function
declarations.
Here is a brief summary of how I have seen people commonly bind this...
this
could be bound to the function in the constructor.
1class Button extends React.Component {2 constructor() {3 ...4 this.toggle = this.toggle.bind(this);5 }67 toggle() {8 ...Some dope logic using this9 }1011 render() {12 ...type up some sweet sweet JSX13 <RandomThing toggleProp={this.toggle} />14 }15}
Or you can define the function as an arrow function on the class
.
1class Button extends React.Component {2 constructor() {3 ...4 }56 toggle = () => {7 ...Some dope logic using this8 }910 render() {11 ...type up some sweet sweet JSX12 <RandomThing toggleProp={this.toggle} />13 }14}
But which is better?
That is really up to preference. I used to prefer bind(this)
because it was
more explicit. Today I prefer arrow functions because they are more common and
reduce noise. Each has its place. But whats more important than how you bind
this
in your React components is understanding why you have to do it in the
first place. A deeper undersanding of lexical and relative scoping in variables
is valuable no matter what you are programming in JavaScript.
I originally wrote this article in 2018. How time flies when it comes to React.
I used to spend hours teaching students about this
in React, and now you
don't have to worry about it at all because Functional Components are far more
common than Class Components. You probably won't need to do a lot of binding in
todays React. But much of this is about this
in general, rather than the
specific case of React, so I hope that you can still glean some useful bits of
knowledge.