Làm thế nào để sử dụng các hàm mũi tên (các trường lớp công khai) làm phương thức lớp?


181

Tôi mới sử dụng các lớp ES6 với React, trước đây tôi đã liên kết các phương thức của mình với đối tượng hiện tại (hiển thị trong ví dụ đầu tiên), nhưng ES6 có cho phép tôi liên kết vĩnh viễn một hàm lớp với một thể hiện của lớp bằng mũi tên không? (Hữu ích khi chuyển dưới dạng hàm gọi lại.) Tôi gặp lỗi khi tôi cố gắng sử dụng chúng như bạn có thể với 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);
  }

Vì vậy, nếu tôi chuyển qua SomeClass.handleInputChange, chẳng hạn setTimeout, nó sẽ nằm trong phạm vi lớp chứ không phải windowđối tượng.


1
Tôi sẽ thích thú khi biết câu trả lời tương tự cho TypeScript
Mars Robertson

Giải pháp của TypeScript giống như đề xuất ES7 (xem câu trả lời được chấp nhận ). Điều này được hỗ trợ bởi TypeScript.
Philip Bulley

2
Đối với tất cả những người kết thúc ở đây, một bài đọc thú vị về chủ đề "các hàm mũi tên trong thuộc tính lớp có thể không tuyệt vời như chúng ta nghĩ"
Wilt

1
Bạn nên tránh sử dụng các hàm mũi tên trong lớp vì chúng sẽ không phải là một phần của nguyên mẫu và do đó không được chia sẻ bởi mọi trường hợp. Nó giống như đưa ra cùng một bản sao của hàm cho mọi trường hợp.
Sourabh Ranka

Câu trả lời:


205

Cú pháp của bạn hơi bị tắt, chỉ thiếu một dấu bằng sau tên thuộc tính.

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

Đây là một tính năng thử nghiệm. Bạn sẽ cần kích hoạt các tính năng thử nghiệm trong Babel để biên dịch nó. Đây là một bản demo với thử nghiệm được kích hoạt.

Để sử dụng các tính năng thử nghiệm trong babel, bạn có thể cài đặt plugin có liên quan từ đây . Đối với tính năng cụ thể này, bạn cần transform-class-propertiesplugin :

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

Bạn có thể đọc thêm về đề xuất cho Trường lớp và Thuộc tính tĩnh tại đây



4
(Mặc dù tôi biết rằng công trình bên ngoài của một lớp ES6) mà không xuất hiện để làm việc cho tôi, babel ném một mũi tên thẻ bất ngờ tại đầu tiên =tạihandleInputChange =
Ben

40
Bạn nên cung cấp một số giải thích, ví dụ: đây là một tính năng thử nghiệm cho đề xuất ES7.
Felix Kling

1
dự thảo thông số kỹ thuật hiện tại đã được thay đổi vào tháng 9, vì vậy bạn không nên sử dụng nó cho autobind như Babel đề xuất.
chico

1
Đối với Babel 6.3.13, bạn cần cài đặt sẵn 'es2015' và 'giai đoạn 1' để biên dịch cái này
Andrew

12
Nó hoạt động, nhưng phương thức được thêm vào thể hiện trong hàm tạo thay vì được thêm vào nguyên mẫu và nó là một sự khác biệt lớn.
lib3d

61

Không, nếu bạn muốn tạo các phương thức ràng buộc, cụ thể, bạn sẽ phải làm điều đó trong hàm tạo. Tuy nhiên, bạn có thể sử dụng các hàm mũi tên cho điều đó, thay vì sử dụng .bindtrên phương thức nguyên mẫu:

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

Có một đề xuất có thể cho phép bạn bỏ qua constructor()và trực tiếp đặt bài tập trong phạm vi lớp với cùng chức năng, nhưng tôi không khuyên bạn nên sử dụng nó vì nó mang tính thử nghiệm cao.

Ngoài ra, bạn luôn có thể sử dụng .bind, cho phép bạn khai báo phương thức trên nguyên mẫu và sau đó liên kết nó với thể hiện trong hàm tạo. Cách tiếp cận này có tính linh hoạt cao hơn vì nó cho phép sửa đổi phương thức từ bên ngoài lớp học của bạn.

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

4

Tôi biết câu hỏi này đã được trả lời đầy đủ, nhưng tôi chỉ có một đóng góp nhỏ để thực hiện (đối với những người không muốn sử dụng tính năng thử nghiệm). Do vấn đề phải liên kết nhiều hàm liên kết trong hàm tạo và làm cho nó trông lộn xộn, tôi đã đưa ra một phương thức tiện ích từng bị ràng buộc và gọi trong hàm tạo, thực hiện tất cả các ràng buộc phương thức cần thiết cho bạn.

Giả sử tôi có lớp này với hàm tạo:

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

Trông thật bừa bộn phải không? Bây giờ tôi đã tạo phương thức tiện ích này

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

Tất cả những gì tôi cần làm bây giờ là nhập tiện ích đó và thêm một cuộc gọi đến hàm tạo của tôi và tôi không cần phải ràng buộc từng phương thức mới trong hàm tạo nữa. Trình xây dựng mới bây giờ trông sạch sẽ, như thế này:

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


Giải pháp của bạn là tốt, tuy nhiên nó không bao gồm tất cả các phương pháp vòng đời trừ khi bạn khai báo chúng trong đối số thứ hai. Ví dụ: shouldComponentUpdategetSnapshotBeforeUpdate
WebDeg Brian

Ý tưởng của bạn tương tự như autobinding, có sự suy giảm hiệu suất đáng chú ý. Bạn chỉ cần liên kết các chức năng mà bạn vượt qua. Xem Medium.com/@charpeni/ từ
Michael Freidgeim

3

Bạn đang sử dụng chức năng mũi tên và cũng ràng buộc nó trong hàm tạo. Vì vậy, bạn không cần phải ràng buộc khi bạn sử dụng các chức năng mũi tên

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

HOẶC bạn cần liên kết một hàm chỉ trong hàm tạo khi bạn sử dụng hàm bình thường như dưới đây

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

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

Cũng không nên ràng buộc một chức năng trực tiếp trong kết xuất. Nó phải luôn luôn trong constructor

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.