BaseComponent / BaseClass for Autobinding methods


(Agusputra) #1

For those who use React with ES6 will write more codes because the methods do not automatically bound to this.

Besides manual binding, binding method in render function is considered antipattern: https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f

This is what we used to do when using ES6 class in React.

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

        //Manual binding methods
        this.func1 = this.func1.bind(this);

        this.state = {foo: 'FOO'};
    }

    func1() {
        alert(`${arguments.callee.name} - ${this.state.foo}`);
    }

    func2() {
        alert(`${arguments.callee.name} - ${this.state.foo}`);
    }

    render() {
        return (
            <div>
                Apps
                <hr/>
                <button onClick={this.func1}>button1</button>

                {/*Manual binding inside render is antipattern*/}
                <button onClick={this.func2.bind(this)}>button2</button>
            </div>
        )
    }
}

ReactDOM.render(<App/>, window.reactRoot);

That’s why I try to create base class aimed to do method binding automatically.

BaseClass or BaseComponent

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

        this.bindMethodsToThis();
    }

    getMethodNames() {
        const excludes = [
            'constructor',
            'componentWillMount',
            'render',
            'componentDidMount',
            'componentWillReceiveProps',
            'shouldComponentUpdate',
            'componentWillUpdate',
            'componentDidUpdate',
            'componentWillUnmount',
            'bindMethodsToThis'
        ];
        const props = Object.getOwnPropertyNames(this.constructor.prototype);
        const names = [];
        for (let p of props) {
            const p2 = this[p];
            if (typeof p2 === 'function' && excludes.indexOf(p) === -1) {
                names.push(p);
            }
        }
        return names;
    }

    bindMethodsToThis() {
        const methods = this.getMethodNames();
        
        methods.forEach((item) => {
            this[item] = this[item].bind(this);
        });
    }
}

And use it like this:

class App extends BaseComponent {
    constructor(props) {
        super(props);

        //No need for manual binding
        //this.func1 = this.func1.bind(this);
        //this.func2 = this.func2.bind(this);

        this.state = {foo: 'FOO'};
    }

    func1() {
        alert(`${arguments.callee.name} - ${this.state.foo}`);
    }

    func2() {
        alert(`${arguments.callee.name} - ${this.state.foo}`);
    }

    render() {
        return (
            <div>
                Apps
                <hr/>
                <button onClick={this.func1}>button1</button>
                <button onClick={this.func2}>button2</button>
            </div>
        )
    }
}

ReactDOM.render(<App/>, window.reactRoot);

ES6 classes, how to avoid the .bind in constructor for event handlers?
(Sophie Alpert) #2

Neat project!

This certainly works, though I would think it’s surprising that the base class has this effect of changing how the binding works. Anyone familiar with ES6 classes and how this works in JavaScript would likely be confused upon seeing your code.

Personally, I’m a bigger fan of the experimental property initializer proposal where you can write handleClick = () => { ... } in the class body to make an autobound method. Another popular proposal is for decorators where you could write @autobind above each method you want to be autobound. Neither of these is finalized and might not make it into the official JS spec, though it’s looking pretty good for both of them so hopefully they’ll make it in.


(Kevin Kirchner) #3

I use the ES7 function bind syntax for binding this.

Then you don’t have to do anything in your constructor if you use ::this.func1:

<button onClick={::this.func1}>button1</button>

FYI: from Babel link above:

Warning: This syntax is highly experimental and you should not use it for anything serious (yet).


(Bruno Mota) #4

@kevnk that’s not ideal. This because each time that button is rendered a new function instance is created. Degrades a bit the performance. And if a component has, for example, a shouldComponentUpdate that check if any prop is different (which is common if you’re working with immutable data and presentational components concept) it will always update since you’re always passing a prop that is different.

What I’ve been using and works great is as @spicyj referred, a bind decorator to the methods I’ll pass to components.


(Agusputra) #5

It is like manual binding, just syntaxtic sugar. And still considered antipattern. Bind method inside render is antipattern. Read this post: https://medium.com/@esamatti/react-js-pure-render-performance-anti-pattern-fb88c101332f


(Sophie Alpert) #6

In most cases the manual binding is a fine alternative and won’t have a performance impact. I wouldn’t worry about it.


(Radu Brehar) #7

I’m biased, but I use https://www.npmjs.com/package/react-class. You can use to solve autobinding in one of the two ways:

  • call autoBind in constructor or
  • extend a base class, which is just a thin wrapper around React.Component