As of November 2017 I know of several ways of binding methods to React Components in order for the this
keyword to point to the React Element that owns the method (necessary in event handlers for example)
1. Bind in constructor
class A extends React.Component {
constructor(props) {
super(props)
this._eventHandler = this._eventHandler.bind(this)
}
_eventHandler() {
// ...
}
render() {
return <div onClick={this._eventHandler} />
}
}
2. Arrow function in render()
class A extends React.Component {
_eventHandler() {
// ...
}
render() {
return <div onClick={()=>{this._eventHandler()}} />
}
}
3. bind in render()
class A extends React.Component {
_eventHandler() {
// ...
}
render() {
return <div onClick={this._eventHandler.bind(this)} />
}
}
4. ES2015 arrow function in class fields
class A extends React.Component {
_eventHandler = () => {
// ...
}
render() {
return <div onClick={this._eventHandler} />
}
}
5. @autobind decorator
class A extends React.Component {
@autobind
_eventHandler() {
// ...
}
render() {
return <div onClick={this._eventHandler} />
}
}
1 is the safest way because it requires no build-time transformations by babel, but it very annoying to type.
2 and 3 have performance implications due to the binding happening on every render and the React diff algorithm
4 and 5 requires a lot less typing than 1, but they require support in babel and might not be part of the final specs yet. Besides that I am very against the idea of annotations (coming from a Java backend background I despise annotations because they are often overused and overly magical)
As of the latest Babel version is either 4 or 5 the recommended and safest (regarding future-compatibility) way of binding functions? Are there any other way I am not aware of? Should I keep using 1? Also if any of these considered safe to use are there any codemods that can change my codebase to use them?
Edit: @LucaFabbri pointed to reflective bind babel transform. It looks pretty cool, but it required a non-standard babel-plugin which I don't like because it is not very future-safe. I try to avoid build-time magic as much as possible, they are fine to use if you work on only one codebase over a long period of time, but if you maintain several codebases you need to handle the build-time magic each time (plus no support in create-react-app without ejecting).
ReactJS bind() Method The bind() is an inbuilt method in React that is used to pass the data as an argument to the function of a class based component.
Binding methods helps ensure that the second snippet works the same way as the first one. With React, typically you only need to bind the methods you pass to other components. For example, <button onClick={this.
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.
The bind () is an inbuilt method in React that is used to pass the data as an argument to the function of a class based component.
In the React docs it says the following [React Component Class] Methods follow the same semantics as regular ES6 classes, meaning that they don't automatically bind this to the instance. But clearly they do, as the second code example I've posted shows.
With React, typically you only need to bind the methods you pass to other components. For example, <button onClick= {this.handleClick}> passes this.handleClick so you want to bind it. However, it is unnecessary to bind the render method or the lifecycle methods: we don’t pass them to other components. This post by Yehuda Katz explains ...
The third built-in Javascript method can do this. The .bind () method is similar to the other two in that you pass it the context you want to bind the function to, but it does not immediately run the function. Instead a copy of the function with the switched context is returned. This copy can then be run whenever you want.
If binding in the constructor (method 1) is too annoying for you, I'd say the preferred method would be an arrow function on the class field (method 4), as it's a simple babel transformation, it's a stage 3 proposal (basically future-proof), and avoids the performance concerns of methods 2 and 3 (if you ever hope to take advantage of shouldComponentUpdate
or PureComponent
.
One method that you may not be aware of is one that I came up with (haven't seen anyone else with anything similar) specifically for avoiding methods 2 and 3 when doing .map
over some array of data to render out a list of components that need to be passed this.someInstanceMethod(withSomeArg)
on props. For example:
class CatList extends React.Component {
static propTypes = {
kitties: PropTypes.arrayOf(PropTypes.instanceOf(Cat)),
}
adoptKitty(cat) {
this.setState({ loading: true })
return api.adopt(cat)
.then(res => this.setState({ loading: false })
.catch(err => this.setState({ loading: false, err })
}
render() {
// ... other stuff ...
{this.props.kitties.map(kitty => (
<PureKittyCat
key={kitty.id}
// ... the problem:
onClick={() => this.adoptKitty(kitty)}
/>
))}
}
}
It's not immediately clear how to avoid passing a function literal on props within a .map
like this, not only because you need to bind this
, but you also need to pass the current element into the instance method. Most people in this situation would just give up the idea of making PureKittyCat
a React.PureComponent
.
My solution to this problem is to store a WeakMap on the Component instance to create a local cache (local to the parent component) that associates each kitty
object with any methods I want to pass to the associated PureKittyCat
component. It looks like this:
class CatList extends React.Component {
static propTypes = {
kitties: PropTypes.arrayOf(PropTypes.instanceOf(Cat)),
}
this.methodCache = new WeakMap()
adoptKitty(cat) {
this.setState({ loading: true })
return api.adopt(cat)
.then(res => this.setState({ loading: false })
.catch(err => this.setState({ loading: false, err })
}
render() {
// ... other stuff...
{this.props.kitties.map(kitty => {
// ... the good stuff:
if ( !this.methodCache.has(kitty) ) {
this.methodCache.set(kitty, {
adopt: () => this.adoptKitty(kitty),
// any other methods you might need
})
}
// as long is this is the same in-memory kitty, onClick will
// receive the same in-memory function object every render
return (
<PureKittyCat
key={kitty.id}
onClick={this.methodCache.get(kitty).adopt}
/>
)
})}
}
}
WeakMaps are the right choice for something like this to avoid a memory-leak. And storing a new cache on the component rather than as a global cache (in its own module) avoids the possibility of running into name collisions if/when you try caching methods of the same name for the same object (here Cat
s) across multiple components.
The only other solution to this problem I've seen is explained here: https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36.
Both involve some annoying setup, but that's really the only way to get achieve rendering optimizations for that scenario.
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