Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

So I am trying to learn JavaScript and/or react and got a little mixed up with understanding .bind(this) in the constructor. However, I think to understand it now, and just want to know,
Why would anyone use Binding vs an Arrow-function in JavaScript?
(or in the onClick event).

Is there any pro/con to use one vs the other?

See below code samples.

Binding method ensures this in clickEvent function references the class:

class Click extends react.Component {
  constructor(props) {
    super(props)
    this.clickEvent = this.clickEvent.bind(this);
  }

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

  clickEvent() { console.log(this) } // 'this' refers to the class
}

However below method also references the class:

class Click extends react.Component {

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

  clickEvent() { console.log(this) } // 'this' refers to the class
}
like image 787
Kyle Woolley Avatar asked May 16 '18 16:05

Kyle Woolley


People also ask

Do we need to bind arrow function in React?

In ReactJs, when we are working with class-based components and want to access this inside a class method. This will need to bind it. Binding this allows it to access the state and setstate inside the class. To avoid the need for binding we have something introduced in ES6 as arrow functions.

Are arrow functions better JavaScript?

Arrow functions shine best with anything that requires this to be bound to the context, and not the function itself. Despite the fact that they are anonymous, I also like using them with methods such as map and reduce , because I think it makes my code more readable.

How do you use arrow instead of bind?

Use arrow functions to avoid binding `this` to methods: Binding this to handleClick in the constructor allows us to use this. setState from Component inside handleClick . Without this binding, this is re-scoped for handleClick and therefore cannot be used with the setState method.

How do I beat onClick as a prop?

When you add an onClick prop to the LinkButton component, it is just a property of an object. By calling props. onClick from inside of that component you are just calling a function that is stored inside of a property, similar to this: let props = { onClick: function () { alert("Executed!"); } }; props.


2 Answers

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)

like image 134
Top-Master Avatar answered Sep 30 '22 01:09

Top-Master


Your second example recreates the function on every render. In your first, you create the bound function just once.

You could just create the handler in the constructor as an arrow function:

class Click extends react.Component {
  constructor(props) {
    super(props)
    this.clickEvent = () => {   // ***
      console.log(this);        // ***
    };                          // ***
  }

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

Using the class fields proposal syntax (which is enabled in the transpiler settings of most React projects, and which you're using for your render function), you can write that like this:

class Click extends react.Component {
  constructor(props) {
    super(props)
  }

  clickEvent = () => {          // ***
    console.log(this);          // ***
  };                            // ***

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

Which is the same thing.


Side note: You're creating a separate render function for each instance of your class. There's no need to do that, it can be on the prototype. So:

class Click extends react.Component {
  constructor(props) {
    super(props)
  }

  clickEvent = () => {
    console.log(this);
  };

  render() {
    return <button onClick={this.clickEvent}>Click Me</button>;
  }
}
like image 43
T.J. Crowder Avatar answered Oct 04 '22 01:10

T.J. Crowder