10

I've read a bunch of articles about the use of () => {} syntax, binding in the constructor, binding in the props etc.. but from what I understand, binding this is costly performance-wise, and doing automatic binding with arrow functions is costly because it creates a new anonymous function every time.

So what is the most performant, 'React way' of handling this issue?

Binding in the constructor seems to work well for functions that don't need to pass parameters, like this:

constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
}

but how do we handle binding functions that pass params, without binding it in the prop, like this:

<li onClick={this.handleClick.bind(this, item.id)} />{item.name}</li>

Does binding this in the constructor and then binding null or undefined in the prop result in a bound function that only binds once?

Feel free to correct me in any misconceptions I have. It seems like the solution to this problem should be more well-known and universal... that is if I haven't just been living under a rock!

EDIT:

Even with abstraction, doesn't the click handler get bound with every single item render?

in the article here, they give this example to avoid binding the click handler, but because React.createClass does autobinding of methods, I don't see how this is not actually binding on every item render?

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);
  }
});

Can someone explain this? Doesn't this just look like it avoids binding every ListItem render, but because of autobinding in React.createClass, it still does?

I tried this example with class List extends Component syntax instead of createClass, and this.handleClick was undefined, because the handleClick method wasn't bound to the class.

At the end of the day, it appears that this just cleans up verbosity, and doesn't actually improve performance by reducing method binding...

2
  • Performance is absolutely not a concern here. You'd have to update thousands of elements at 60fps before you saw any performance problems, and React itself would be your bottleneck, not garbage collection, so it doesn't matter. Commented Dec 9, 2016 at 17:49
  • I didn't realize that. Thanks for your comment! Commented Jan 13, 2017 at 21:27

5 Answers 5

9

If passing a parameter then you must bind in the render function and not in the constructor. This can be done using the bind or an arrow function.

<li onClick={this.handleClick.bind(this, item.id)} />{item.name}</li>

or

<li onClick={() => this.handleClick(item.id)} />{item.name}</li>
Sign up to request clarification or add additional context in comments.

2 Comments

I understand that, as I've shown in my question, but I'm wanting to know if there is a way to not have to bind for each item in the render
@jakeaaron No there is not. I'd recommend using arrow functions
8

For your <li onClick={this.handleClick.bind(this, item.id)} />{item.name}</li>

this typically means you need another layer of abstraction IE a new react component that returns the same element but you can pass in the onClick as a prop as well as item id as a prop. Then in that component you would call this.props.onClick(this.props.id) or however you format the data.

this article draws out all the differences between each way of binding instance methods as well as how each impacts performance https://medium.com/@housecor/react-binding-patterns-5-approaches-for-handling-this-92c651b5af56#.

2 Comments

hey, thanks for your answer! I read that article and I've edited my question to address a concern! Please take a look! I appreciate your help
if you are using ES6 i would suggest either use the right babel configuration to be able to bind your instance methods using way #4 or #5 in the article you posted. When you bind using these ways the binding is only done once, when the component is instantiated. As far as the React.createClass, it is similar fashion, when the component is instantiated the instance methods are autobinded to that instance. It doesn't have to create a new binding per ListItem because the function itself is already binded to the instance if that makes sense
0

Does this work? (Using TypeScript)

<Input type="password" onChange={this.onPasswordInputChange('primary')} />;
<Input type="password" onChange={this.onPasswordInputChange('secondary')} />;


interface IOnPasswordInputChange {
  (value: string): void;
}

private onPasswordInputChange = (type: string): IOnPasswordInputChange => {
  return (value: string) => {
    this.setState({ [type]: value });
  };
}

Comments

0

Syntactically, I much prefer arrow functions to binding (especially since only arrow functions facilitate passing extra arguments to event handlers). Understanding the performance penalty associated with arrow functions, I created a utility function that accepts arrow function definitions which will be cached for the lifetime of a component:

utils.js

export function createLambdaCache() {
  const result = {};
  return (key, lambda) => {
    if (!(key in result)) {
      result[key] = lambda;
    }
    return result[key];
  };
}

component.js

class MyComponent extends React.Component {
  render() {
    return (
      ...  
      // below 'btnEvent' string is used as a key to cache the event reference for subsequent renders
      <button onClick={this.bindWithArgs('btnEvent', evt => this.clickHander(id, evt))} />
      ...
    );
  }
  clickHandler(id, evt) {
    console.log(id, evt);
  }
  bindWithArgs = createLambdaCache();
}

typescript version of above createLambdaCache:

type evtHandler<E> = (evt: E) => void;
export function createLambdaCache<T = any>() {
  const result: {[key in keyof T]: evtHandler<any>} = {} as any;
  return <E>(key: keyof T, lambda: evtHandler<E>) => {
    if (!(key in result)) {
      result[key] = lambda;
    }
    return <evtHandler<E>>result[key];
  };
}

Comments

-1
onDelete(e) {
    const id = e.target.dataset.id;
}

<DropdownItem data-id={item.id} onClick={this.onDelete}><i className="fa fa-trash" /> Remove</DropdownItem>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.