React, Class Syntax, and Autobinding Shenanigans!


#1

One of the big things the React team announced as part of React 15.5 and React 16 is that the old React.createClass approach for creating components is no longer recommended:

var Foo = React.createClass({
  render: function() {
    return (
      <h1>Hello!</h1>
    );
  }
});

The approach instead is to use the class syntax:

class Foo extends React.Component {
   render() {
      return (
           <h1>Hello!</h1>
       );
   }
}

Overall, this change is fine! It’s time we all moved to use the class-based syntax anyway. There is one major annoyance, though. When using createClass, React autobinds function to the component. This allows you to access the component via this.componentName. With the class approach, you don’t get autobinding. You have to bind functions and other things you care about manually…like an animal!

Here is an example:

class LightningCounter extends React.Component {
    constructor(props) {
        super(props);
        
        this.state = {
            strikes: 0
        };
    }

    timerTick() {
        this.setState({
            strikes: this.state.strikes + 100
        });
    }

    componentDidMount() {
        setInterval(this.timerTick, 1000);
    }
    
    render() {
        return (
            <h1>{this.state.strikes}</h1>
        );
    }
}

class LightningCounterDisplay extends React.Component {
    render() {
        var divStyle = {
            width: 250,
            textAlign: "center",
            backgroundColor: "black",
            padding: 40,
            fontFamily: "sans-serif",
            color: "#999",
            borderRadius: 10
        };

        return(
            <div style={divStyle}>
                <LightningCounter/>
            </div>
        );
    }
}

ReactDOM.render(
    <LightningCounterDisplay/>,
    document.querySelector("#container")
);

This is the code behind the Dealing with State tutorial. When the code runs, in an ideal world, the timerTick function will get called and the code inside it will execute. Reality is that the timerTick function gets called. The this.state and other things inside it have no idea what they are bound to. They will return undefined errors when you try to use them.

There are two solutions that you can use that don’t require additional 3rd party libraries. One solution is where you explicitly bind timerTick in the constructor:

class LightningCounter extends React.Component {
    constructor(props) {
        super(props);
        
        this.state = {
            strikes: 0
        };

        this.timerTick = this.timerTick.bind(this);
    }
    .
    .
    .
}

One other solution, is to use arrow functions:

timerTick = () => {
    this.setState({
        strikes: this.state.strikes + 100
    });
}

Both of these approaches will get your code to work. The value of this will be bound properly and all the properties you try to access will resolve correctly.

If you want a more automated solution that relies on another library, then react-autobind might be up your alley: https://www.npmjs.com/package/react-autobind

With all of this said, this is a step backwards compared to React.createClass. There are ways to handle this automatically as part of the React library. Or maybe there isn’t. Who knows :stuck_out_tongue:

Cheers,
Kirupa


#2

React.Component could autobind in its constructor if they really wanted to. It’s probably better that they’re not doing this for performance, but it would be a way to make this concern go away.

I think ultimately, language-wise, the intention is that you’d to be able to bind or not using decorators:

@autobind
class LightningCounter extends React.Component {
    ...
}

or per method:

class LightningCounter extends React.Component {

    @autobind
    timerTick() {
        this.setState({
            strikes: this.state.strikes + 100
        });
    }
    ...
}

autobind itself I don’t think would be built into the language, but implementations exist out there already like https://www.npmjs.com/package/autobind-decorator

Decorators are in stage 2 right now, so they might be a little while longer before being official, but TypeScript already implements them (and has for a while).


#3

My main preference for it being done automatically is that it reduces the amount of extra steps a developer needs to keep in mind when building an app. Autobinding feels like something everybody would want by default, and the extra work should be disable it if you don’t want.

I believe React did do autobinding early on when the ES6 syntax was gaining traction, but they moved away from that in later versions. The performance reason that you mention might be why.

:yum:


#4

ECMA TC39 could have chosen to go with bound class methods by default. They did in ES4, then reversed for ES6. React’s flip-flopping is a direct result.

@autobind is a bit of a bandaid; it should probably be called @bind/@bound. I think including auto in the name makes it sound like more work is being done than is actually being done.

I think this is a good argument for it to have been a language-level decision to bind methods. Historically, that didn’t make a lot of sense when ES6 was being standardized.

I think it’s a mistake that arrow functions bind differently than non-arrow functions. It’s odd to swap => for function when I want to swap binding semantics; both are fine for single- and multi-line- functions in certain situations.