10

I'm in the process of updating a React component to ES6 and suffered the problem described in this question - Unable to access React instance (this) inside event handler - namely not binding to the component instance.

That made sense and of course worked, but I'm confused about the other part of the answer:

Be aware that binding a function creates a new function. You can either bind it directly in render, which means a new function will be created every time the component renders, or bind it in your constructor, which will only fire once.

constructor() {
  this.changeContent = this.changeContent.bind(this);
}

vs

render() {
  return <input onChange={this.changeContent.bind(this)} />;
}

I'm assuming that binding in the constructor is the preferred approach for performance etc, but you know what they say about assume!

What are the trade-offs for these two approaches? Is there ever a situation where one is definitely better than the other? Or does it not matter?

Community
  • 1
  • 1
MattDuFeu
  • 1,615
  • 4
  • 16
  • 25

3 Answers3

12

Downside of binding in the constructor: react hot loader won't work.

Downside of binding in render(): performance.


Recently I've been doing this. It's slightly faster than binding in render, but I'm willing to trade the performance for flexibility and my coveted HMR.

render(){
  return <input onChange={(e) => this.handleChange(e.target.value)}>;
}

It gives a little more flexibility, for example, and easier transition to the canonical Input atom.

render(){
  return <input onChange={(x) => this.handleChange(x)}>;
}

Or adding arguments where you want them:

render(){
  return (
    <ul>
      {this.props.data.map((x, i) => {
        // contrived example
        return (
          <li 
            onMouseMove={(e) => this.handleMove(i, e.pageX, e.pageY)}>
          {x}
          </li>
        );
      }}
    </ul>
  );
}
Brigand
  • 84,529
  • 20
  • 165
  • 173
  • Your very first point is enough to convince me to leave it in render(). If performance becomes a problem, I can always swap it for production. – MattDuFeu Jul 09 '15 at 12:18
  • I'm not using hot-loader in my project, should I use that. All the prerequisites (webpack, etc) are there, just wanted to know what major enhancements I can get by hot-loader? – Abhinav Singi Sep 09 '15 at 09:17
7

I think all you've to understand is Function.prototype.bind() will return a new function. So you'll basically be doing a creation every time by performing the binding action in the render() method. Chances of the render() method being called multiple times is really high.

So doing that in the constructor means you end up binding only once and you can re-use it as many times as you want. Even if the render() method is called multiple times the same function created with a different bound context will be used.

Yes, ideally you should bind in the constructor. Reminds me of a piece of code (check the constructor) I was going through a couple of weeks back.

Rishabh
  • 1,901
  • 2
  • 19
  • 18
2

I think you've addresses the main problems to do with recreating functions. I'd like to highlight another option using arrow functions and property initializers. The arrow functions in this case will automatically adopt the local this.

e.g.

class MyClass extends React.Component {
  changeComponent = (e) => {
    // this will refer to the component
  }

  render = () => {
    return <input onChange={this.changeContent} />;
  }
}

You can read more about it here: http://babeljs.io/blog/2015/06/07/react-on-es6-plus/

When you have many functions you'd want to bind, this may be a better solution. You do lose the cleanness of just using a standard function declaration, though.

rbhalla
  • 869
  • 8
  • 32
  • 1
    This might be slower than .bind in the constructor because it has to create the functions for each instance instead of just .bind on the same function. But of course this is probably negligible :-) – Brigand Jul 08 '15 at 19:49
  • Will that problem exist when native implementations of es6 arrive in browsers? Or is that only a problem when transpiling from es6 to es5? @FakeRainBrigand – rbhalla Jul 08 '15 at 22:26
  • Well this isn't valid es6 so... you'll get a syntax error :-p But performance varries across engines and versions of those engines, so there's no right answer to that. – Brigand Jul 09 '15 at 01:57
  • @FakeRainBrigand sorry you're right, this is a planned proposal for property initialisers. I've amended my answer to take that into account. Probably makes this answer completely unusable for the moment but I'll keep it here anyway. – rbhalla Jul 09 '15 at 12:04
  • It's useable by those that don't mind experimental features. I've seen other people doing it exactly like this. – Brigand Jul 10 '15 at 02:02