화살표 함수 (공개 클래스 필드)를 클래스 메서드로 사용하는 방법은 무엇입니까?


181

나는 React와 함께 ES6 클래스를 처음 사용하고 있는데, 이전에는 메소드를 현재 객체에 바인딩했지만 (첫 번째 예 참조) ES6을 통해 클래스 함수를 화살표가있는 클래스 인스턴스에 영구적으로 바인딩 할 수 있습니까? (콜백 함수로 전달할 때 유용합니다.) 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);
  }

SomeClass.handleInputChange예를 들어 내가 전달한다면 객체가 setTimeout아닌 클래스 인스턴스로 범위가 지정됩니다 window.


1
TypeScript에
Mars Robertson

TypeScript의 솔루션은 ES7 제안서와 동일합니다 ( 허용 된 답변 참조 ). 이것은 기본적으로 TypeScript에서 지원됩니다.
Philip Bulley


1
화살표 함수는 프로토 타입의 일부가 아니므로 모든 인스턴스가 공유하지 않으므로 클래스에서 화살표 함수를 사용하지 않아야합니다. 모든 인스턴스에 동일한 기능의 사본을 제공하는 것과 같습니다.
Sourabh Ranka

답변:


205

구문 이름이 약간 떨어져 속성 이름 뒤에 등호가 없습니다.

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

이것은 실험적인 기능입니다. 이것을 컴파일하려면 Babel의 실험 기능을 활성화해야합니다. 다음 은 실험이 가능한 데모입니다.

babel에서 실험 기능을 사용하려면 여기 에서 관련 플러그인을 설치할 수 있습니다 . 이 특정 기능을 사용하려면 transform-class-properties플러그인 이 필요합니다 .

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

클래스 필드 및 정적 속성 제안에 대한 자세한 내용은 여기를 참조하십시오.



4
(나는 ES6 클래스 밖에서 작동한다는 것을 알고 있지만) 나를 위해 작동하지 않는 것처럼, babel은 처음 =에 예기치 않은 토큰 화살표를 던집니다.handleInputChange =
Ben

40
예를 들어 ES7 제안의 실험적인 기능이라는 몇 가지 설명을 제공해야합니다.
Felix Kling

1
현재 사양 초안은 9 월에 변경되었으므로 Babel이 제안한대로 자동 바인딩에 사용해서는 안됩니다.
chico

1
Babel 6.3.13의 경우이를 컴파일하려면 사전 설정 'es2015'및 'stage-1'이 필요합니다.
Andrew

12
작동하지만 메서드는 프로토 타입에 추가되는 대신 생성자의 인스턴스에 추가되며 큰 차이가 있습니다.
lib3d

61

아니요, 바인딩 된 인스턴스 별 메소드를 작성하려면 생성자에서이를 수행해야합니다. 그러나 .bind프로토 타입 방법 을 사용 하는 대신 화살표 기능을 사용할 수 있습니다 .

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

당신이 생략 할 수 있는 제안 이 있습니다constructor()동일한 기능을 사용하여 과제 과제를 수업 범위에 직접 넣을 있지만 실험적이기 때문에 사용하지 않는 것이 좋습니다.

또는 항상을 사용 .bind하여 프로토 타입에서 메서드를 선언 한 다음 생성자의 인스턴스에 바인딩 할 수 있습니다. 이 접근법은 클래스 외부에서 메소드를 수정할 수 있기 때문에 유연성이 뛰어납니다.

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

4

이 질문에 대한 답변이 충분하다는 것을 알고 있지만 실험 기능을 사용하지 않으려는 사람들에게는 약간의 기여를합니다. 생성자에서 여러 함수 바인딩을 바인딩하고 지저분한 것처럼 보이게 만드는 문제 때문에 한 번 생성자에서 바인딩되고 호출 된 유틸리티 메소드가 나타났습니다. 필요한 모든 메소드 바인딩이 자동으로 수행됩니다.

생성자와 함께이 클래스가 있다고 가정합니다.

//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
}

지저분 해 보이지 않습니까? 이제이 유틸리티 메소드를 만들었습니다.

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

이제 내가해야 할 일은 해당 유틸리티를 가져 와서 생성자에 대한 호출을 추가하는 것이므로 생성자에서 각각의 새로운 메소드를 더 이상 바인딩 할 필요가 없습니다. 새로운 생성자는 이제 다음과 같이 깨끗해 보입니다.

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


해결책은 훌륭하지만 두 번째 인수로 선언하지 않는 한 모든 수명주기 방법을 다루지는 않습니다. 예를 들면 : shouldComponentUpdategetSnapshotBeforeUpdate
WebDeg 브라이언

아이디어는 자동 바인딩과 유사하며 성능이 현저하게 저하됩니다. 전달하는 함수 만 바인딩하면됩니다. 참조 medium.com/@charpeni/...을
마이클 Freidgeim에게

3

화살표 함수를 사용하고 있으며 생성자에 바인딩합니다. 따라서 화살표 기능을 사용할 때 바인딩을 할 필요가 없습니다.

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

또는 아래와 같이 일반 함수를 사용할 때 생성자에서만 함수를 바인딩해야합니다.

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

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

또한 렌더링에서 직접 함수를 바인딩하는 것은 권장되지 않습니다. 항상 생성자에 있어야합니다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.