Arrow functions, context and why they're so important in React.js

An unintuitive concept, but these are fundamentally different

<button onClick={this.runFunction}//>
//NOT THE SAME
<button onClick={() => this.runFunction()}//>

Try it out yourself later in a component, where this.runFunction uses this.setState. The first will throw an error. The other will not. Theres a short explanation and a long one. To someone who has no idea why these two are different, I think a long explanation is in order.

function and its this play by its own rules

The defintion of context in a function does not obey lexical scoping unless you tell it to. If you give it no declarations, it assumes this is the window object.

Strict declaration of context

this.myFunction // 'this' is my context, where my 'this' resulting declaration will obey lexical scoping

function ConstructorExample() {

  this.testAnonThis = function() { //this.testThis binds this function to my constructor's context
    let anonThis = (function(){
      return this
    })() //anonymous function. THIS HAS NO BINDING. Remember, not lexically scoped.
    console.log(`anonThis is the window object?: ${window === anonThis}`)
  }
}
new ConstructorExample().testAnonThis()
//anonThis is the window object?: true

Why do function-based anonymous functions have this === window?

JavaScript assigns context very late. As mentioned before, the assignment of this in an anonymous function does not obey lexical scoping. Some see it as a strength, because you can reuse functions and keep things lean, while others call it unintuitive and flawed by design.

Arrow functions have no this

So important to understand, that it hurts. I need to explicitly state it:

functions assigned using function have a context automatically asigned: (this). Arrow function do not have a this

So how does this apply to React.js?

10 years later, I'm going to explain that original statement.

<button onClick={this.runFunction}/>
This is assigned using function, which has its own context.
  <button onClick={()=> this.runFunction()}/>
Assigned as an arrow function, which has a this that is inherited from the React component class.

My personal opinion: Just use arrow functions, all the time.

It's important to understand fundamentals. But at the end of the day, the ECMAScript team introduced arrow functions for a very good reason: They make so much sense.

In fact, they make sense to the point where you don't even have to know what's going on to use them flawlessly. In my opinion: an incredible strength, but a minor detriment. The future will consist of developers that don't understand fundamentals, such as myself, from 3 months ago. But you will never run into context issues if your event handlers are arrow functions.

Yes, I know I didn't explain how to fix the function-based assignment

Brainless answer: use bind.

<button onClick={this.runFunction.bind(this)}/> //proper scope

If I somehow not been talking to myself for the past 7 blog posts and any viewers want to understand this topic, feel free to leave me a comment and I'll go over bind, call, and apply.

Comments

Popular posts from this blog

Contrasting var and ES6s new declarations: let and const

Modularity vs. Abstraction. Which is More Important?

Unit testable components pt.3: Testing components