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 so there’s no performance penalty.
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 because a new function is being created every time it re-renders (which could be pretty often).
class Button extends React.Component {
handleClick() {
console.log('clickity');
}
render() {
return (
<button onClick={this.handleClick.bind(this)}/>
);
}
}
Here’s another variant that is just as bad, 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 {
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.
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.
Don't Use Bind When Passing Props was originally published by Dave Ceddia at Angularity on July 05, 2016.
CodeProject