How to use arrow functions (public class fields) as class methods?

How to use arrow functions (public class fields) as class methods?

I’m new to using ES6 classes with React, previously I’ve been binding my methods to the current object (show in first example), but does ES6 allow me to permanently bind a class function to a class instance with arrows? (Useful when passing as a callback function.) I get errors when I try to use them as you can with CoffeeScript:
class SomeClass extends React.Component {

// Instead of this
constructor(){
this.handleInputChange = this.handleInputChange.bind(this)
}

// Can I somehow do this? Am i just getting the syntax wrong?
handleInputChange (val) => {
console.log(‘selectionMade: ‘, val);
}

So that if I were to pass SomeClass.handleInputChange to, for instance setTimeout, it would be scoped to the class instance, and not the window object.

Solutions/Answers:

Solution 1:

Your syntax is slightly off, just missing an equals sign after the property name.

class SomeClass extends React.Component {
  handleInputChange = (val) => {
    console.log('selectionMade: ', val);
  }
}

This is an experimental feature. You will need to enable experimental features in Babel to get this to compile. Here is a demo with experimental enabled.

Related:  Remove all spaces between Chinese words with regex

To use experimental features in babel you can install the relevant plugin from here. For this specific feature, you need the transform-class-properties plugin:

{
  "plugins": [
    "transform-class-properties"
  ]
}

You can read more about the proposal for Class Fields and Static Properties here


Solution 2:

No, if you want to create bound, instance-specific methods you will have to do that in the constructor. However, you can use arrow functions for that, instead of using .bind on a prototype method:

class SomeClass extends React.Component {
  constructor() {
    super();
    this.handleInputChange = (val) => {
      console.log('selectionMade: ', val, this);
    };
    …
  }
}

There is an proposal which might allow you to omit the constructor() and directly put the assignment in the class scope with the same functionality, but I wouldn’t recommend to use that as it’s highly experimental.

Alternatively, you can always use .bind, which allows you to declare the method on the prototype and then bind it to the instance in the constructor. This approach has greater flexibility as it allows modifying the method from the outside of your class.

class SomeClass extends React.Component {
  constructor() {
    super();
    this.handleInputChange = this.handleInputChange.bind(this);
    …
  }
  handleInputChange(val) {
    console.log('selectionMade: ', val, this);
  }
}

Solution 3:

I know this question has been sufficiently answered, but I just have a small contribution to make (for those who don’t want to use the experimental feature). Because of the problem of having to bind multiple function binds in the constructor and making it look messy, I came up with a utility method that once bound and called in the constructor, does all the necessary method bindings for you automatically.

Related:  React-Native Updating List View DataSource

Assume I have this class with the constructor:

//src/components/PetEditor.jsx
import React from 'react';
class PetEditor extends React.Component {
  
   constructor(props){
        super(props);
        this.state = props.currentPet || {tags:[], photoUrls: []};
     
        this.tagInput = null;
        this.htmlNode = null;

        this.removeTag = this.removeTag.bind(this);
        this.handleChange = this.handleChange.bind(this);
        this.modifyState = this.modifyState.bind(this);
        this.handleKeyUp = this.handleKeyUp.bind(this);
        this.addTag = this.addTag.bind(this);
        this.removeTag = this.removeTag.bind(this);
        this.savePet = this.savePet.bind(this);
        this.addPhotoInput = this.addPhotoInput.bind(this);
        this.handleSelect = this.handleSelect.bind(this);
        
    }
    // ... actual method declarations omitted
}

It looks messy, doesn’t it?
Now I created this utility method

//src/utils/index.js
/**
 *  NB: to use this method, you need to bind it to the object instance calling it
 */
export function bindMethodsToSelf(objClass, otherMethodsToIgnore=[]){
    const self = this;
    Object.getOwnPropertyNames(objClass.prototype)
        .forEach(method => {
              //skip constructor, render and any overrides of lifecycle methods
              if(method.startsWith('component') 
                 || method==='constructor' 
                 || method==='render') return;
              //any other methods you don't want bound to self
              if(otherMethodsToIgnore.indexOf(method)>-1) return;
              //bind all other methods to class instance
              self[method] = self[method].bind(self);
         });
}

All I now need to do is import that utility, and add a call to my constructor, and I don’t need to bind each new method in the constructor anymore.
New constructor now looks clean, like this:

//src/components/PetEditor.jsx
import React from 'react';
import { bindMethodsToSelf } from '../utils';
class PetEditor extends React.Component {
    constructor(props){
        super(props);
        this.state = props.currentPet || {tags:[], photoUrls: []};
        this.tagInput = null;
        this.htmlNode = null;
        bindMethodsToSelf.bind(this)(PetEditor);
    }
    // ...
}

Solution 4:

You are using arrow function and also binding it in constructor. So you no need to do binding when you use arrow functions

class SomeClass extends React.Component {
  handleInputChange = (val) => {
    console.log('selectionMade: ', val);
  }
}

OR you need to bind a function only in constructor when you use normal function like below

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

  handleInputChange(val){
    console.log('selectionMade: ', val);
  }
}

Also binding a function directly in render is not recommended. It should always be in constructor

Related:  How to setup route for websocket server in express?