Before the release of React v0.13 using methods in React components was very straightforward since the traditional React.createClass
automatically bounded user defined methods to the correct this
context.
However, since the introduction of ES6 classes in React components, the methods are no longer automatically bound. As a result, we have at least four ways to handle this
context in React. Let’s consider the advantages and disadvantages of each.
Bind in render
This approach works by allocating this
to a given function on each render
call. The downside of this approach is that the function is reallocated on each render. Whilst for most applications the performance implications of this are negligible, it's still something to keep in mind.
class LogThis extends Component {
handleClick() {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick.bind(this)}>
Click me
</button>
);
}
}
Arrow functions in render
This approach makes use of ES6 fat-arrow functions which help preserve the context of this
by using lexical scoping. For those non-familiar with lexical scoping, it just means that a function uses this
from the code that contains the arrow function.
Since this
in render
function always refers to the containing React component, and fat-arrow function uses lexical scoping, handleClick
retains the component's this
.
It stil has the downside of the previous approach in that a diffrent callback is created on each rerender and thus may cause performance issues.
class LogThis extends Component {
handleClick() {
console.log('this is:', this);
}
render() {
return (
<button onClick={e => this.handleClick(e)}>
Click me
</button>
);
}
}
Bind in constructor
One way to avoid performance downsides of previous two approaches is to bind in the constructor. This approach is currently recommended by the official React documentation and it's also the approach I have adopted on my projects.
The main downside of this approach is having to repeatedly bind each function in the constructor. If there are several functions in the component to be bound, it may start looking ugly quick.
class LogThis extends Component {
constructor(props) {
super(props);
this.state = { message: 'Hello, world!' };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
Class fields
The final way to bound this
is by using experimental public class fields syntax.
This approach is the most superior of all because it avoids the performance issues of reallocating functions on each render. And it also removes unnecessary repetition when binding functions in the constructor.
One notable disadvatange of this approach is that by declaring methods in such a way, handleClick
is not declared in the prototype and thus is impossible to be called via super.handleClick
from the derived class.
class LogThis extends Component {
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
Extra: use react-autobind
The final extra and from what I can tell, a non-conventional way to handle binding in React ES6 classes is by using npm module react-autobind
. It has all the advantanges of binding in constructor, plus you get to avoid repetition by having to bind each function separately.
The disadvantage is that an additional npm module has to be imported and you still have to call autoBind
in the constructor.
import autoBind from 'react-autobind';
class LogThis extends Component {
constructor() {
super();
autoBind(this);
}
handleClick() {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
Summary
I personally see no real benefits of using binding or arrow functions in render. There are no real readability benefits as far as I'm concerned and the performance is negatively impacted, even if negligibly.
Thus, if you are willing to use Babel stage-2
preset and will not have to call a parent function from derived class, I would recommend using class fields approach to binding.
Otherwise, I would recommend binding in the constructor either by binding each function manually, or using react-autobind
module. Both are fine and comes down to personal preference. If there are several functions to be bound, I would use react-autobind
and if it's just one or two, go with the conventional approach.
Top comments (0)