React v0.13 introduced a major change in how you can define your component classes. What was a React-bound class system was slimmed down to a level where the pure JavaScript classes can be used. It has many benefits - your component classes are not ‘magical’ anymore, you can take advantage of the common JavaScript idioms and so on.

This change emphasized that React is JavaScript - and React authors don’t want to escape this truth. That change encourages you to use language features, not custom solutions to create your React code. For people coming from different background than JavaScript / Node.js using some language features can be confusing.

One of those confusing features is function context binding in JavaScript. In this blogpost I want to explain how and why you need to do it.

Let’s start with JavaScript

As has been said before, React does not hide its JavaScript roots. That’s why it’s important to understand how JavaScript handles the function context assignment.

In languages like Ruby or Java, this (or in case of Ruby self) will always point to the object in which your method is defined. So in Ruby if you are working on the foo method inside the Bar class, self will always point to the object which is the instance of the Bar class.

JavaScript works quite surprisingly here. Because in JavaScript function context is defined while calling the function, not while defining it! This is what can surprise many when coming to JS from different fields. Such late binding is a powerful mechanism which allows us to re-use loosely coupled functions in variety of contexts.

There are four patterns of invoking functions that defines the context of the function being called:

  • function invocation pattern
  • method invocation pattern
  • constructor invocation pattern
  • apply invocation pattern

All of these patterns used define function context differently. In next paragraphs you’ll see how.

Function invocation pattern

If there are no dots in your function call, your context is likely to be window.

The most straightforward way to call a function is to call it directly:

var func = function() {
  // ...
};

func()

Calling the function this way will set its context (this) to a global variable of an environment on which your JavaScript operates. In browsers it is a window global variable.

It can be very counter-intuitive. Let’s see an another example:

var unicorns = {
  func: function() { // ... }
};

var fun = unicorns.func;
fun();

The object.func function is a method of the unicorns object. Since JavaScript treats functions like every other value, it can be assigned to the variable. Usually people think that such assignment cannot change the context of the function - it’ll still be unicorns, right? Wrong. Since context binding is determined by the way you call this function, the context will change to window global variable. Oops!

The less surprising behaviour can be achieved by using the method invocation pattern of calling functions.

Method invocation pattern

If there are dots in your function call, your function context will be the right-most element of your dots chain.

If your function is defined within an object, calling it directly from an object will set its context to the object on which the function is being called.

var frog = {
  RUN_SOUND: "POP!!",
  run: function() {
    return this.RUN_SOUND;
  }
};

frog.run(); // returns "POP!!" since this points to the `frog` object.
var runningFun = frog.run;
runningFun(); // returns "undefined" since this points to the window

If your object is nested, when calling a function from an object nested within another object, the most nested object will be set as a context of the function call.

var foo = {
  bar: {
    func: function() { ... }
  }
};

foo.bar.func(); // this will point to `bar`

This is less surprising for people coming from another languages. It is very similar to behaviour of the most programming languages.

This pattern works well if you have one object and want to have your context defined properly. If you are instantiating objects, you’d like to have this pointing to each instance separately. Let’s say hi to constructor invocation pattern!

Constructor invocation pattern

Every time you see a new followed by a function name, your this will point to a newly created empty object.

Function that are designed to be used with the new syntax are called constructors. By convention their names begin by an upper case letter.

function Wizard() {
  this.castSpell = function() { return "KABOOM"; }
}

Calling it directly will cause window to be mutated (since it’ll be a function invocation pattern). But this function is intended to be called with a new operator.

function Wizard() {
  this.castSpell = function() { return "KABOOM"; };
}

var merlin = new Wizard(); // this is set to an empty object {}. Returns `this` implicitly.
merlin.castSpell() // returns "KABOOM";

This is exactly how a constructor invocation pattern looks like. Function called with the new operator will cause two things:

  • Function will have a this context pointing to an empty object.
  • If you do not specify return, or this function will return a non-object value, this will get returned from such function.

This pretty sums up all options you have in JavaScript when it comes to implicit setting a function call context. There is another very powerful technique for setting contexts - since functions are also objects in JavaScript, they have methods to change function call context to something else.

Apply invocation pattern

When you have a reference of the function in hand, you can call the function with the context provided by you. It can be done by using two methods that functions provide:

  • call - it takes a context as the first argument. The rest of arguments are arguments passed to the function being called this way.
  • apply - it takes a context as the first argument and an array of arguments for the function being called as the second argument.

Let’s see those methods in action!

function addAndSetX(a, b) {
  this.x += a + b;
}

var obj1 = { x: 1, y: 2 };

addAndSetX.call(obj1, 1, 1); // this = obj1, obj1 after call = { x: 3, y : 2 }
// It is the same as:
// addAndSetX.apply(obj1, [1, 1]);

This is very handy if you need to call function being passed from somewhere else (for example, as an argument to your function) with a certain context. It is not very usable with async callback though - since binding is coupled with a function call. To set proper context with callbacks, you may need yet another handy technique - you can create a bounded function from it.

Binding functions

Bounded function in JavaScript is a function that is bounded to a given context. That means no matter how you call it, the context of the call will stay the same. The only exception is the new operator which always return a new context.

To create a bounded function out of the regular function, the bind method is used. bind method take context to which you want to bind your function as a first argument. The rest of arguments are arguments that will be always passed to such function. It returns a bounded function as a result. Let’s see an example:

function add(x, y) {
  this.result += x + y;
}

var computation1 = { result: 0 };
var boundedAdd = add.bind(computation1);

boundedAdd(1, 2); // `this` is set to `computation1`.
                  //  computation1 after call: { result: 3 }

var boundedAddPlusTwo = add.bind(computation1, 2);
boundedAddPlusTwo(4); // `this` is set to `computation1`.
                      // computation1 after call: { result: 9 }

Bounded function can’t be changed even by manually calling call and apply! See these examples:

var obj = { boundedPlusTwo: boundedAddPlusTwo };
obj.boundedPlusTwo(4); // `this` is set to `computation1`.
                       // even though method is called on `obj`.
                       // computation1 after call: { result: 15 }
var computation2 = { result: 0 };
boundedAdd.call(computation2, 1, 2); // `this` is set to `computation1`.
                                     // even though context passed to call is
                                     // `computation2`
                                     // computation1 after call: { result: 18 }

You are now equipped with this knowledge about the JavaScript. Let’s jump to your React component classes and see how function call context affects your code.

How and what to bind

ECMAScript 2015 (ECMAScript 6) introduced a new class syntax that can be used to create React component classes. In fact, this class syntax is a syntactic sugar for the old, prototype system of object-oriented JavaScript.

That means function context calls within ES2015 classes follow the same principles as the rest of JavaScript.

You can think (to simplify things - it’s not exactly true how ES2015 classes are defined. ES2015 classes use prototypes and Object.defineProperty under the hood. But in this case, semantics are the same) that such class:

class Foo {
  constructor() {
    this.x = 2;
    this.y = 4;
  }

  bar() {
    // ...
  }

  baz() {
    // ...
  }
}

Is roughly the same as:

function Foo() {
  this.x = 2;
  this.y = 4;

  this.bar = function() { // ... };
  this.baz = function() { // ... };
}

Remember it is just a simplification. In the case of determining the function context calls, this more sophisticated logic follows the same principles as the snippet above.

React.createClass

In the oldest component class syntax called React.createClass binding problem is non-existent. That is because React.createClass performs auto-binding under the hood. All methods you define in an object passed to React.createClass will be automatically bound to the component instance. That means you can always use setState, access props and state and so on from these methods.

This may seem cool, but that means under the hood something magical happened which is not under your control. While it may be perfectly acceptable in 99% of cases, it limits your ability to arbitrary set context - which can be a big issue in more sophisticated codebases.

ECMAScript 2015 classes

In ECMAScript 2015 classes you need to bind your methods manually. In fact, all you actually need to bind are event handlers and functions passed down as properties (a.k.a. callbacks). Why is that?

Just think about it. In the React codebase, React is assuming that you have render method within your component class. While rendering, your component is instantiated from the component class by using the constructor invocation pattern (a.k.a new Component(...)). Since your component classes inherit from React.Component, functions like setState and forceUpdate are inherited.

What React codebase can know about your component and is able to perform calls using the method invocation pattern to it is:

  • You may have component lifecycle methods - it just calls it under the hood with component.componentDidUpdate(...) - so this is properly binded to a component instance itself.
  • You must have a render method - so it is too called with a method invocation pattern. Most non-event handler functions are called from render, so you have full control over the pattern you use - and you should generally use this.method pattern since it binds context correctly.

But event handlers you pass as properties (like onChange or onClick) can come from many sources. They can be even passed from the non-React level by a property of the top-level component. In React.createClass React assumes that they come from your component and binds them automaticaly. But in ES2015 classes you have freedom. Under the hood they are called using the function invocation pattern. You can check it by yourself here. Just click on a “Hello” word on the Output pane and see that this points to window. You may need to open your browser’s developer console to see that.

That means by default you can’t access properties, state and component methods like setState from event handlers. To do so, you need to bind them explicitly.

The best place to bind your event handlers is your constructor:

class InputExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = { text: '' };
    this.change = this.change.bind(this);
  }

  change(ev) {
    this.setState({ text: ev.target.value });
  }

  render() {
    let { text } = this.state;
    return (<input type="text" value={text} onChange={this.change} />);
  }
}

This way your event handler has its context bound to the component instance. You can access props and state and call setState or forceUpdate from such bound handler.

ECMAScript 2015 classes with class properties

There is an experimental feature called class properties that can help you avoid binding methods explicitly. It is a syntactic sugar for defining fields and functions within a constructor. It looks like this:

class InputExample extends React.Component {
  state = { text: '' };

  // ...
}

And is compiled to something like this:

class InputExample extends React.Component {
  constructor(...arguments) {
    super(...arguments);

    this.state = { text: '' };
  }

  // ...
}

So how about defining a method this way?

class InputExample extends React.Component {
  state = { text: '' };

  change = function(ev) {
    this.setState({ text: ev.target.value });
  };

  // ...
}

So now, you got an equivalent of the following class:

class InputExample extends React.Component {
  constructor(...arguments) {
    super(...arguments);

    this.state = { text: '' };
    this.change = function(ev) {
      this.setState({ text: ev.target.value });
    };
  }

  // ...
}

Well, it does not help you at all with the function context problem. this.change will still get called by a function invocation pattern, so the context will still be wrong.

In fact this feature itself is not helpful in this case. But combined with another feature, called arrow functions syntax it fixes the problem with binding in a very elegant way.

The arrow function syntax is a way of defining function with a syntax like this:

change = (ev) => this.setState({ text: ev.target.value });

It is a more concise way than writing a function(ev) { .... } statement. If you don’t provide { and } brackets after the => arrow, such function is a single expression which is returned instantly. So this desugars to something like:

change = function(ev) { return this.setState({ text: ev.target.value }); };

If that would be the case, you’d still have a problem with binding event handlers. But there is another very useful side-effect - arrow functions always gets the context from where they have been defined. So in fact, this example is equivalent to:

var that = this;
change = function(ev) { return that.setState({ text: ev.target.value }); };

All this occurencies within the arrow function will get transformed into that - and that is a reference to the context in which this function is defined. Combine it with the class properties behaviour and you get:

class InputExample extends React.Component {
  constructor(...arguments) {
    super(...arguments);

    this.state = { text: '' };
    var that = this;
    this.change = function(ev) {
      that.setState({ text: ev.target.value });
    };
  }

  // ...
}

This that trick is a very common JavaScript idiom. It is used when you want to call a function using the function invocation pattern, but still have an access to the higher level context. It is useful in modules and closures in JavaScript. It’s a technique certainly worth to know!

So, defining the InputExample component class with class properties and arrow functions allows you to get rid of binding altogether. Let’s see the final result:

class InputExample extends React.Component {
  state = { text: '' };
  change = ev => this.setState({text: ev.target.value});

  render() {
    let {text} = this.state;
    return (<input type="text" value={text} onChange={this.change} />);
  }
}

Your component is written in a more concise way, you get rid of the constructor and your event handler is properly bound without making the explicit binding. It looks very promising!

The downside of this solution is that class properties are still in an experimental stage. That means this feature can be removed in later iterations of ECMAScript 2016 (a.k.a ECMAScript 7 or ES7) without warning. It can also be removed by maintainers of your EcmaScript transpiler. One of the transpilers that is widely used in React community and allows you to enable this feature is Babel.js.

In my opinion using this syntax has more advantages than disadvantages. Class properties syntax is widely adopted already and if you have your codebase tested your tests will detect if something happen with this feature. You can also desugar this syntax very easily if you need to. Overall gain is huge, so for me it’s a way to go with defining React component classes.

Summary

Knowing how JavaScript function call contexts behaves is a very valuable knowledge - it allows you to track subtle bugs that can arise while developing your UI using JavaScript. Since React is JavaScript, component classes follows the same principles of call contexts than rest of your codebase. Apart from the surprise effect you can get when coming from another languages, having your React component classes defined with pure ES2015 classes makes your React codebase behaviour more consistent with the rest of your codebase. Good luck!

Examples - one more time

Before (ES 2015)

class InputExample extends React.Component {
  constructor(props) {
    super(props);

    this.state = { text: '' };
    this.change = this.change.bind(this);
  }

  change(ev) {
    this.setState({ text: ev.target.value });
  }

  render() {
    let { text } = this.state;
    return (<input type="text" value={text} onChange={this.change} />);
  }
}

After (class properties + arrow function syntax)

class InputExample extends React.Component {
  state = { text: '' };
  change = ev => this.setState({text: ev.target.value});

  render() {
    let {text} = this.state;
    return (<input type="text" value={text} onChange={this.change} />);
  }
}
comments powered by Disqus