React and ES6 - Part 3, Binding to methods of React class (ES7 included)

http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html

This is the third post of series in which we are going to explore the usage of React with ECMAScript6 and ECMAScript7.

You could find links to all parts of series below:

The code corresponding to this article is available atGitHub.

_Update from 18.06.2016:_Updated the code and text to use React 15 and Babel 6.

If you look at paragraph “CartItem render method” of theprevious article in the seriesyou might be confused by the usage of{this.increaseQty.bind(this)}.

If we try the same example with just{this.increaseQty}we’ll seeUncaught TypeError: Cannot read property 'setState' of undefinedin browser console:

This is because when we call a function in that way binding tothisis not a class itself, it’sundefined. It’s default JavaScript behavior and is quite expected. In opposite to this, in case you useReact.createClass()all the methods are autobinded to the instance of an object. Which might be counter-intuitive for some developers.

No autobinding was the decision of React team when they implemented support of ES6 classes for React components. You could find out more about reasons for doing so inthis blog post.

Let’s now see various ways of how to call class methods from your JSX in case you use ES6 classes. All the different methods we consider here are available atthis GitHub repository.

Method 1. Using of Function.prototype.bind().

We’ve already seen this:

export default class CartItem extends React.Component {
    render() {
        <button onClick={this.increaseQty.bind(this)} className="button success">+</button>
    }
}

As any method of ES6 class is plain JavaScript function it inheritsbind()from Function prototype. So now when we callincreaseQty()inside JSX,thiswill point to our class instance. You could read more about Function.prototype.bind() in thisMDN article.

Method 2. Using function defined in constructor.

This method is a mix of the previous one with usage of class constructor function:

export default class CartItem extends React.Component {

    constructor(props) {
        super(props);
        this.increaseQty = this.increaseQty.bind(this);
    }

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

You don’t have to usebind()inside your JSX anymore, but this comes with the cost of increasing the size of constructor code.

Method 3. Using fat arrow function and constructor.

ES6 fat arrow functionspreservethiscontext when they are called. We could use this feature and redefineincreaseQty()inside constructor in the following way:

export default class CartItem extends React.Component {

    constructor(props) {
        super(props);
        this._increaseQty = () => this.increaseQty();
    }

    render() {
        <button onClick={_this.increaseQty} className="button success">+</button>
    }
}

Method 4. Using fat arrow function and ES2015+ class properties.

Additionally, you could use fat arrow function in combination with experimental ES2015+ class properties syntax:

export default class CartItem extends React.Component {

    increaseQty = () => this.increaseQty();

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

So, instead of defining our class method in a constructor as in method number 3, we use property initializer.

Warning:Class properties are not yet part of current JavaScript standard. But your are free to use them in Babel using corresponding experimental flag (stage 0 in our case). Your could read more about how to do this inBabel documentation.

We’ve already switched to stage 0 inReact and ES6 - Part 2, React Classes and ES7 Property Initializers, so this is not an issue for our example.

Method 5. Using ES2015+ function bind syntax.

Quite recently Babel introduced syntactic sugar forFunction.prototype.bind()with the usage of::. I won’t go into details of how it works here. Other guys already have done the pretty good explanation. You could refer tothis official Babel blog postfor more details.

Below is code with usage of ES2015+ function bind syntax:

export default class CartItem extends React.Component {

    constructor(props) {
        super(props);
        this.increaseQty = ::this.increaseQty;
        // line above is an equivalent to this.increaseQty = this.increaseQty.bind(this);
    }

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

Again, this feature is highly experimental. Use it at your own risk.

Method 6. Using ES2015+ Function Bind Syntax in-place.

You also have a possibility to use ES2015+ function bind syntax directly in your JSX without touching constructor. It will look like:

export default class CartItem extends React.Component {
    render() {
        <button onClick={::this.increaseQty} className="button success">+</button>
    }
}

Very concise, the only drawback is that this function will be re-created on each subsequent render of the component. This is not optimal. What is more important that will cause problems if you use something like PureRenderMixin (or its equivalent for ES2015 classes).

Conclusion

In this article, we’ve discovered various possibilities of binding class methods of React components. I’ve prepared test project based on code from part 2 of this series. It’s availablehere.

Next time we’ll see what is the state of React mixins for ES2015 classes.

Further Reading

results matching ""

    No results matching ""