React, ES6, Autobinding, and createClass()

TL;DR React removed "autobinding" in ES6 component classes. The code onClick={ this.onClickHandler } could fail inside onClickHandler because it's not bound to anything. Let's see why.

ES6 Classes?

React has a utility method to create components called React.createClass(). It handles mixins and "autobinds" methods.

The newest Javascript language features include a true object oriented "Class" syntax, which React now supports. It's more future proof, and means React stays closer to vanilla Javascript. It's a Good Thing™. However, we lose "autobinding," which is confusing to new users.

Methods vs. Functions

A method is a function declared as an object property. A function is standalone, not attached to any object. This naming convention is used more in other languages, but it has real world consequences in Javascript.

For methods, this inside the method body is the object the method is attached to:

var obj = {  
    prop: 'Hello',
    sayHello: function() {
        console.log( this.prop );
    }
};

obj.sayHello(); // Logs "Hello"  

Nice and simple. But now let's change how we invoke sayHello().

var reference = obj.sayHello;  
reference(); // logs "undefined"  

Why did it log undefined? Javascript functions are said to be "first class," meaning you can pass them around like any other data type. When we store a reference to sayHello, we lose the relationship to obj. We're invoking it as a function, not as a method.

When a function references this in the function body, it will be the global scope. This is usually never what you want.

Key concept: obj.sayHello() is a method, because it's specifically written as a "object dot method." This dot notation tells Javascript that inside sayHello, this will be obj.

Setting this in a Function

Let's look at a solution to this contrived example:

var obj = {  
    prop: 'Hello',
    sayHello: function() {
        console.log(this.prop);
    }
};

var newFunction = obj.sayHello.bind(obj);  
newFunction(); // logs "Hello"  

We make a new function where this is always obj. That's what bind() does. It's important to note method.bind(this) doesn't call method. It creates a new function. When we call the new function, this will be the first argument of bind.

You can read more about bind() at the MDN documentation.

When Does This Apply to React?

You don't need to bind all methods. It only matters when you pass methods to someone else. Consider this component:

class Home extends React.Component {

    update() {
        this.setState({
            newStuff: true
        });
    }

    render() {
        return <div
            onClick={ this.update }
        />;
    }

}

This fails on click, with "this.setState is not a function." When update is called, it's invoked as a function, so this won't be set correctly. Even though we pass it with the dot notation, whoever calls update only has access to the reference.

Solution 1: Bind in render()

render() {  
    return <div
        onClick={ this.update.bind( this ) }
    />
}

Pros:

  • Easy to drop into existing codebase.

Cons:

  • Every time render() is called, bind() creates a new function instance. It's more function instances to garbage collect. It's unlikely it will ever cause performance issues unless you're updating thousands of components at 60 FPS.
  • You have to repeat this.update.bind(this) every time you use update in render().

Solution 2: Bind in constructor()

You can pre-bind all your methods in the component's constructor:

class Home extends React.Component {

    constructor() {
        super()
        this.update = this.update.bind(this);
    }

Pros:

  • No changes needed in the render() function.
  • If using underscore, you can simply do _.bindAll( this, 'update', ... ).

Cons:

  • Easy to forget to bind a method.
  • Have to track which methods need binding.
  • Your methods won't hot reload. When Webpack hot reloads React components, it doesn't execute the constructor again. Method instances will be stale.

Solution 3: Use the Autobind Decorator

This is the solution I'm currently using. The autobind-decorator lets you annotate which class methods are automatically bound with this:

import autobind from 'autobind-decorator'

class Home extends React.Component {  
    @autobind
    update() {
        ...
    }

Pros:

  • Easy to read and understand at a glance.
  • Unlike binding in constructor, method instances will hot reload with Webpack.

Cons:

  • Takes some work to set up with Babel.
  • Decorators aren't standardized yet in Javascript syntax. They could change or be removed entirely. This is less future-proof.

Solution 4: Fat Arrow Class Methods

There's an ES7 Proposal that lets you declare class methods as fat arrow functions. It's supported by Babel when using the stage-0 preset. Note the specific syntax:

class Home extends React.Component {  
    update = () => {
        ...
    }

Pros:

  • No third party packages required (like autobind).
  • Methods will hot reload.

Cons:

Solution 5: Keep Using createClass()

React still supports React.createClass() for components! It's not deprecated and it's fine to use.

Pros:

  • No changes required to existing codebase.

Cons:

  • Strays from "vanilla Javascript," gives React a larger API surface area (more to learn for newcomers).
  • Allows for mixins, which React is moving away from. Could encourage mixin use.
  • Less future proof as the React API evolves.

That's It!

If this post helped you understand React and method binding, consider following me on Twitter or buying me a coffee :).

comments powered by Disqus