Don't Use Bind When Passing Props

Dave Ceddia bio photo By Dave Ceddia Comment

Don't call .bind when passing props

There are many situations when writing React where you’ll want to pass a function to a prop. Usually it’s to pass a callback down to a child component so that the child can notify the parent of some event.

It’s important to keep in mind the binding of the function – what its this object will point to when it’s called.

There are a few ways to make sure the binding is correct, some better than others. This post will go over the options.

Way #1: Autobinding (good, only with React.createClass)

If you’re using React.createClass, the member functions in your component are automatically bound to the component instance. You can freely pass them around without calling bind, and you’re always passing the same exact same function.

var Button = React.createClass({
  handleClick: function() {
    console.log('clickity');
  },
  render: function() {
    return (
      <button onClick={this.handleClick}/>
    );
  }
});
Way #2: Calling .bind Within render (bad, ES6)

When using ES6 classes, React does not automatically bind the member functions inside the component.

Binding at the last second like this is one way to make it work correctly, but it will hurt performance slightly because a new function is being created every time it re-renders (which could be pretty often).

The trouble isn’t really that creating a function is an expensive operation. It’s that by creating a new function every time, the component you’re passing it to will see a new value for that prop every time. When it comes time to tune performance by implementing shouldComponentUpdate, that constantly-changing prop will make it look like something changed when really it’s the same as before.

class Button extends React.Component {
  handleClick() {
    console.log('clickity');
  }

  render() {
    return (
      <button onClick={this.handleClick.bind(this)}/>
    );
  }
}

Here’s another variant that is doing the same thing, creating a function every time render is called:

class Button extends React.Component {
  handleClick() {
    console.log('clickity');
  }

  render() {
    var handleClick = this.handleClick.bind(this);
    return (
      <button onClick={handleClick}/>
    );
  }
}
Way #3: Arrow Function in render (bad, ES6)

Similar to the above example, except this one uses an arrow function instead of calling bind. It looks nicer, but it still creates a function every time render is called! No good.

class Button extends React.Component {
  handleClick() {
    console.log('clickity');
  }

  render() {
    return (
      <button onClick={() => this.handleClick()}/>
    );
  }
}
Way #4: Class Instance Field With an Arrow Function (good, ES8+)

This method works by setting handleClick to an arrow function one time when the component is created. Inside render and in other functions, this.handleClick can be passed along without fear because the arrow function preserves the this binding.

This one is labelled “ES8+” because it’s not technically part of ES6 or ES7 (aka ES2016). ES2016 has been finalized and only includes Array.prototype.includes and the exponentiation operator, so if and when this makes it into the spec, it’ll likely be ES2017 (ES8) or beyond.

Even though this is supported by Babel, there’s a (small) risk that this feature could be taken out of the spec and require some refactoring, but a lot of people are using it so it seems likely that it’ll stay put.

class Button extends React.Component {
  // Use an arrow function here:
  handleClick = () => {
    console.log('clickity');
  }

  render() {
    return (
      <button onClick={this.handleClick}/>
    );
  }
}
Way #5: Binding in the Constructor (good, ES6)

You can set up the bindings once in the constructor, and then use them forevermore! Just don’t forget to call super.

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

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

  handleClick() {
    console.log('clickity');
  }

  render() {
    return (
      <button onClick={this.handleClick}/>
    );
  }
}
Way #6: Using Decorators (good, ES8+)

There’s a nice library called autobind-decorator which makes it possible to do this:

import autobind from 'autobind-decorator';

class Button extends React.Component {
  @autobind
  handleClick() {
    console.log('clickity');
  }

  render() {
    return (
      <button onClick={this.handleClick}/>
    );
  }
}

The @autobind decorator binds the handleClick method and you’re all set. You can even use it on the entire class, if you’re lazy:

import autobind from 'autobind-decorator';

@autobind
class Button extends React.Component {
  handleClick() {
    console.log('clickity');
  }

  handleOtherStuff() {
    console.log('also bound');
  }

  render() {
    return (
      <button onClick={this.handleClick}/>
    );
  }
}

Once again, ES2016/ES7 doesn’t include this feature so you’re accepting a bit of risk by using it in your code, even though Babel does support it.

Bonus: Passing Arguments Without Bind

As Marc mentioned in the comments, it’s pretty common to use .bind to preset the arguments for a function call, especially in lists, like this:

var List = React.createClass({
  render() {
    let { handleClick } = this.props;
    return (
      <ul>
        {this.props.items.map(item =>
          <li key={item.id} onClick={handleClick.bind(this, item.id)}>
            {item.name}
          </li>
        )}
      </ul>
    );
  }
});

As explained here, one way to fix this and avoid the bind is to extract the <li> into its own component that’ll call the click handler you pass in, with its id:

var List = React.createClass({
  render() {
    let { handleClick } = this.props;
    // handleClick still expects an id, but we don't need to worry
    // about that here. Just pass the function itself and ListItem
    // will call it with the id.
    return (
      <ul>
        {this.props.items.map(item =>
          <ListItem key={item.id} item={item} onItemClick={handleClick} />
        )}
      </ul>
    );
  }
});

var ListItem = React.createClass({
  render() {
    // Don't need a bind here, since it's just calling
    // our own click handler
    return (
      <li onClick={this.handleClick}>
        {this.props.item.name}
      </li>
    );
  },

  handleClick() {
    // Our click handler knows the item's id, so it
    // can just pass it along.
    this.props.onItemClick(this.props.item.id);
  }
});

A Note on Performance

There’s a tradeoff with most of these methods: more (and more complex) code in exchange for some theoretical performance benefit.

“Premature optimization is the root of all evil,” said Donald Knuth. So… before you split up or complicate your code to save a few cycles, actually measure the impact: pop open the dev tools and profile the code and use the React performance tools.

Wrap Up

That about covers the ways to bind the functions you’re passing to props. Know of any other ways? Got a favorite one? Let us know in the comments.

comments powered by Disqus