Function context in bound events in React

Written by CalinLeafshade | Published 2017/01/05
Tech Story Tags: javascript | react | programming

TLDRvia the TL;DR App

One of the dirty irritations in JavaScript is function context, that is to say what is the meaning of the this keyword in any particular function. This is particularly galling in an environment where one is likely to pass around functions as variables a lot (like JavaScript for example).

When making React components we come across this problem pretty much every time we make a component with some kind of user interaction. Consider the following code:

If you try and run this code, you’ll get a exception thrown:

Cannot read property ‘setState’ of undefined

This is specifically thrown from line 8 of the code above. It’s caused by the fact that the value of this isn’t set when the React event system calls your passed function. There are two common fixes to this problem and I think both have problems.

The first is to bind the event to an arrow function instead. This preserves the entire calling context which includes ‘this’, ‘super’ and a number of other things not preserved in a normal function expression. That looks like this:

<button onClick={() => this.handleClick()}>Click Me!</button>

Because this preserves the calling context it will work just fine. There are two problems with this method though. Firstly you would need to pass in all the correct arguments from the arrow function, essentially acting as a proxy function (...args somewhat mitigates this but it’s messy) and secondly it creates a new arrow function every time render() is called which, in a React app, is a lot. We don’t need all that extra garbage.

Another alternative is to use a bound function. This is a function in which the value of this is forced to whatever you like but it’s usually used to preserve the current calling context as shown below:

<button onClick={this.handleClick.bind(this)}>Click Me!</button>

This essentially creates a kind of wrapper for handleClick which sets the value of this before calling it. By doing this though we are still creating a new function every time render() is called.

A solution to all these problems is to use something available in ES7 (via babel) called a Property Initializer. (A small correction from Dan Abramov via twitter: **“**Class Properties aren’t part of ES7. They are a stage 2 proposal. No guarantee that they will get through.” Thanks, Dan) What’s particularly useful to us is that Property Initializers are only evaluated when the class is instantiated so if we initialize a class property to an arrow function, the value of this will be preserved. Neat, huh?

This means we:

  • Don’t have to pass arguments in a proxy object
  • Don’t create a new function every time render() is called.

The code looks like this:

You’ll also notice that I’ve assigned the initial state as a Property Initializer as well so we can just get rid of the whole constructor as a bonus!

As an aside, if you’re wondering why I passed a function to setState instead of a delta object as is usually shown, follow me on twitter to read my next article 😃.

If you liked this article please remember to click the_💚_ symbol below to make sure more people get to read it!

Steven Poulton is a web developer and technical architect living in Manchester, England. In his spare time he likes to make indie music, make indie games and play with his indie cats.


Published by HackerNoon on 2017/01/05