Binding vs Arrow-function (in JavaScript, or for react onClick)

First of all, let’s start with examples of each technique!
But the difference is more related to JavaScript language itself.

Binding:

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.clickHandler = this.clickHandler.bind(this);
  }

  clickHandler() {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}

Arrow-function:

import React from 'react';

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }

  clickHandler = () => {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}

Pros and Cons:

Using the Arrow-function on public-class-field is more human-readable,
because of fewer lines of code,
But keep in mind that using Arrow-function can affect two things:

First the memory and performance; When you use a class field to define a function, your whole method resides on each instance of the class and NOT on the prototype, but using the bind technic, just a small callback is stored on each instance, which calls your method that is stored on the prototype.

Second thing that can be affected is how you write your unit tests.
You won’t be able to use the component prototype to stub on function calls like below:

const spy = jest.spyOn(MyComponent.prototype, 'clickHandler');
// ...
expect(spy).toHaveBeenCalled();

One would have to find another way to stub the method, for example, either by passing a dummy-callback in props, or checking the state changes.

Conclusion

Computers are really good at reading code; you shouldn’t worry about that.
You may want to consider making your code more human-readable by using a class-property arrow-function.


Other tools:

If you want to keep both human-readability and performance, consider using plugin-transform-arrow-functions plugin (although v7.2.0 caused problems for me), just run npm i --save-dev @babel/plugin-transform-arrow-functions and add it into your “babel.config.js” or “.babelrc” file, like:

{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "decoratorsBeforeExport": false }],
    ["@babel/plugin-transform-arrow-functions", { "spec": true }]
  ]
}

Or you could use something like auto-bind decorator, which would turn above example into:

import React from 'react';

import { boundMethod as bind } from 'autobind-decorator';

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
  }

  @bind
  clickHandler() {
    console.log( this )
  }

  render() {
    return <button onClick={this.clickHandler}>Click Me</button>
  }
}

Note that it’s unnecessary to put @bind on every function. You only need to bind functions which you pass around. e.g. onClick={this.doSomething} Or fetch.then(this.handleDone)

Leave a Comment