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

前端 未结 4 1120
隐瞒了意图╮
隐瞒了意图╮ 2020-11-21 22:41

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

相关标签:
4条回答
  • 2020-11-21 22:57

    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

    0 讨论(0)
  • 2020-11-21 23:05

    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);
      }
    }
    
    0 讨论(0)
  • 2020-11-21 23:20

    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.

    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


    0 讨论(0)
  • 2020-11-21 23:22

    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.

    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);
        }
        // ...
    }

    0 讨论(0)
提交回复
热议问题