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?
bind(something) returns a new function, in which references to this will refer to something . This is a way of saving the current value of this , which is in scope during the call to the constructor, so that it can be used later when the function is called.
When we bind the this of the event handler to the component instance in the constructor, we can pass it as a callback without worrying about it losing its context. Arrow functions are exempt from this behavior because they use lexical this binding which automatically binds them to the scope they are defined in.
To avoid the need for binding we have something introduced in ES6 as arrow functions. Using the arrow function to call this. setState will lead to avoid the use of bind.
[React Component Class] Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind this to the instance.
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>
);
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With