2015-07-11 6 views
117

Ich bin neu bei der Verwendung von ES6-Klassen mit React, zuvor habe ich meine Methoden an das aktuelle Objekt gebunden (siehe erstes Beispiel), aber erlaubt es ES6, eine Klassenfunktion dauerhaft an eine Klasseninstanz mit Pfeilen zu binden? (Nützlich, wenn man vorbei als Callback-Funktion.) Bekomme ich Fehler, wenn ich versuche sich, wie Sie mit Coffeescript nutzen können:Wie werden Pfeilfunktionen (öffentliche Klassenfelder) als Klassenmethoden verwendet?

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

Also, wenn ich bin SomeClass.handleInputChange passieren, zum Beispiel setTimeout, wäre es scoped werden zu der Klasseninstanz und nicht das Objekt window.

+1

ich interessiert wäre, die gleiche Antwort für ** Typoskript zu wissen ** –

+0

Typoskript-Lösung ist die gleiche wie die ES7 Vorschlag (siehe [ akzeptierte Antwort] (http://stackoverflow.com/a/31362350/619602)). Dies wird nativ von TypeScript unterstützt. –

Antwort

150

Ihre Syntax ist etwas aus, nur ein Gleichheitszeichen nach dem Eigenschaftsnamen fehlt.

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

Dies ist eine experimentelle Funktion. Sie müssen experimentelle Funktionen in Babel aktivieren, damit diese kompiliert werden können. Here ist eine Demo mit experimentellem aktiviert.

Um experimentelle Funktionen in babel zu verwenden, können Sie das entsprechende Plugin von here installieren. Für diese spezielle Funktion, müssen Sie die transform-class-properties plugin:

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

Sie können mehr über den Vorschlag für Klassenfelder und statischen Eigenschaften lesen here


+4

(Obwohl ich weiß, dass das außerhalb einer ES6-Klasse funktioniert), scheint das für mich nicht zu funktionieren. Babel wirft einen unerwarteten Token-Pfeil auf das erste '=' at 'handleInputChange =' – Ben

+0

Das ist seltsam, als ich darüber gelesen habe der Babel-Blog, siehe [hier] (http://babeljs.io/blog/2015/06/07/react-on-es6-plus/#arrow-functions) – Guy

+0

Ah, es sieht so aus, als müsste man experimentell aktiviert haben. Siehe die [docs] (http://babeljs.io/docs/usage/experimental/) und eine Codedemo mit experimentell aktiviertem [demo] (https://babeljs.io/repl/#?experimental=true&evaluate=) true & los = false & spec = false & code = Klasse% 20PostInfo% 20Extends% 20React.Component% 20% 7B% 0A% 09HandleOptionsButtonClick% 20% 3D% 20 (e)% 20% 3D% 3E% 20% 7B% 0A% 20% 20% 20 % 20this.setState (% 7BshowOptionsModal% 3A% 20wahr% 7D)% 3B% 0A% 20% 20% 7D% 0A% 7D) – Guy

46

Nein, wenn Sie gebunden erstellen möchten, instanzspezifische Methoden müssen Sie das im Konstruktor tun. Sie können jedoch Pfeil Funktionen für die Verwendung anstelle von .bind an einem Prototyp-Methode:

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

Es ein proposal ist, die Ihnen die constructor() und direkt die Zuordnung im Klassenbereich mit der gleichen setzen weglassen könnte erlauben Funktionalität, aber ich würde nicht empfehlen, das zu verwenden, da es sehr experimentell ist.

Alternativ können Sie immer .bind verwenden, mit dem Sie die Methode für den Prototyp deklarieren und dann an die Instanz im Konstruktor binden können. Dieser Ansatz bietet eine größere Flexibilität, da die Methode von außerhalb der Klasse geändert werden kann.

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

Kann jemand mit den Auswirkungen auf die Leistung sprechen, wenn man es auf diese Weise macht, statt auf einen Prototyp zu binden? –

+0

@TylerCollier: Es sollte nicht viel Unterschied sein. Wenn Sie absolute Geschwindigkeit benötigen, ist keine der beiden eine gute Option. Aber afaik 'bind' ist marginal langsamer als Schließungen, weil es zusätzliche Haushaltsführung benötigt. Wie auch immer, Pfeilfunktionen sind seit ES6 idiomatischer, was Grund genug sein sollte, sie zu verwenden. – Bergi

+1

Ok, danke re: Geschwindigkeit und ich bekomme, was Sie über idiomatische Grund genug Grund zu denken. Allerdings: "Arrow-Funktion sind mehr idiomatisch", sicher, aber ich würde nicht sagen, alle (Pfeil) Funktionen im Konstruktor hinzufügen ist noch idiomatisch, und ich hoffe, es ist nie. –

0

Die Frage ist ganz bereit, beantwortet aber ich werde hier einige Anwendungsfälle hinzufügen ES6 verwenden Methode Pfeil.

Innerhalb Callback-Methoden:

let arr = [1,2]; 
let arr1 = arr.map((val,index) => { 
    return val * 2; 
}); 

oder

let arr1 = arr.map(val => { // if one argument then no need of using() 
    return val * 2; 
}); 

Hinweis: innerhalb Callback-Funktion, wenn this dann hier verwendet wird this siehe Funktion anstelle von Komponentenklasse zurückrufen.

Ex:

arr.map(function(val) { 
    this.func(val); // Will not work 
}); 

arr.map((val,index)=>{ 
    this.func(); // Will work 
}); 
+0

Explizite Rückgabe ist in ES6 Pfeilfunktionen nicht erforderlich, und Ihre Beispiele könnten (wohl) sauberer umgeschrieben werden als 'let arr1 = arr.map ((val, index) => val * 2) 'oder' leth arr1 = arr. map (val => val * 2) 'wenn Sie den Iterator-Index nicht benötigen. –

1

weiß, dass ich diese Frage ausreichend beantwortet wurde, aber ich habe nur einen kleinen Beitrag (für diejenigen, die nicht wollen, die experimentelle Funktion verwenden) zu machen. Wegen des Problems, mehrere function binds im Konstruktor zu binden und es chaotisch aussehen zu lassen, habe ich eine Utility-Methode entwickelt, die im Konstruktor gebunden und aufgerufen wird und automatisch alle notwendigen Methodenbindungen für Sie ausführt.

Angenommen ich diese Klasse mit dem Konstruktor:

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

Es sieht chaotisch, nicht wahr? Jetzt habe ich diese Hilfsmethode

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

Alles, was ich jetzt tun müssen, ist, dass Dienstprogramm zu importieren, und um einen Anruf zu meinem Konstruktor hinzufügen, und ich brauche nicht jede neue Methode zu binden im Konstruktor nicht mehr. New Konstruktor sieht jetzt sauber, wie folgt aus:

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