Die Listener von Stores ändern sich bei componentWillUnmount nicht?

7

Ich schreibe eine einfache App auf reactjs-flux und alles funktioniert gut, außer dass ich eine Warnung von reactjs erhalte, die mir sagt, dass ich setState auf nicht eingebundenen Komponenten anrufe.

Ich habe herausgefunden, dass Change-Listener, an die Komponenten angehängt sind, nicht aus dem Store von componentWillUnmount entfernt werden. Ich weiß es, weil, wenn ich die Liste der Listener von Eventemitter drucke, sehe ich den Listener, der zerstört werden sollte, immer noch dort, und die Liste wird größer, wenn ich die gleiche Komponente mehrmals mounte / unmount.

Ich füge Code aus meinem BaseStore ein:

%Vor%

Ich füge den relevanten Code von einer Komponente ein, die diesen Fehler hat (es passiert jedoch mit allen Komponenten):

%Vor%

Das macht mich jetzt ziemlich verrückt. Irgendwelche Ideen?

Danke!

    
Gerard Clos 20.08.2015, 14:39
quelle

7 Antworten

21

Kurzversion: expect(f.bind(this)).not.toBe(f.bind(this));

Längere Erklärung:

Die Ursache des Problems ist, dass EventEmitter.removeListener erfordert, dass Sie eine Funktion übergeben, die Sie zuvor mit EventEmitter.addListener registriert haben. Wenn Sie einen Verweis auf eine andere Funktion übergeben, handelt es sich um ein stilles No-Op.

In Ihrem Code übergeben Sie this._onChange.bind(this) an addListener. bind gibt eine neue Funktion zurück, die an diese gebunden ist. Sie verwerfen dann den Verweis auf diese gebundene Funktion. Dann versuchen Sie, eine andere neue Funktion zu entfernen, die durch einen Bind-Aufruf erstellt wurde, und es ist ein No-Op, da dieser nie hinzugefügt wurde.

React.createClass bindet automatisch Methoden ein. In ES6 müssen Sie Ihren Konstruktor manuell binden:

%Vor%

Es gibt verschiedene Möglichkeiten, die Bindung zu vereinfachen: Sie können einen ES7 @autobind Methoden-Decorator (z. B. autobind-decorator auf npm) verwenden oder eine automatische Bindungsfunktion schreiben, die Sie im Konstruktor mit autoBind(this); aufrufen.

In ES7 können Sie (hoffentlich) Klasseneigenschaften für eine bequemere Syntax verwenden. Sie können dies in Babel aktivieren, wenn Sie als Teil des Vorschlags der Stufe 1 Ссылка möchten. Dann deklarieren Sie Ihre Event-Listener-Methoden einfach als Klasseneigenschaften und nicht als Methoden:

%Vor%

Da der Initialisierer für _onChange im Kontext des Konstruktors aufgerufen wird, bindet die Pfeilfunktion this automatisch an die Klasseninstanz, sodass Sie this._onChange einfach als Ereignishandler übergeben können, ohne sie manuell binden zu müssen.

    
TomW 10.12.2015, 11:18
quelle
4

Also habe ich die Lösung gefunden, es stellte sich heraus, dass ich this._onChange.bind(this) nur einer internen Eigenschaft zuweisen musste, bevor ich es als Argument an removechangelistener und addchangelistener übergab. Hier ist die Lösung:

%Vor%

Ich weiß jedoch nicht, warum dies das Problem löst. Irgendwelche Ideen?

    
Gerard Clos 21.08.2015 09:30
quelle
1
%Vor%

Ich verwende genau dieselbe Implementierung für mehrere reaktive Komponenten. Dies wird für mehrere .jsx-Komponenten wiederholt.

%Vor%

Mögliche Lösung

Momentan funktioniert das Folgende für mich, aber es war ein bisschen temperamentvoll. Wickeln Sie den Aufruf in eine Funktion / benannte Funktion zurück.

%Vor%

könnte man auch versuchen

%Vor%

Theorie

EventEmitter ist aus irgendeinem Grund verwirrt, weil er den zu entfernenden Rückruf identifiziert. Die Verwendung einer benannten Funktion hilft vielleicht dabei.

    
Emile 24.11.2015 12:06
quelle
0

Versuchen Sie, den .bind(this) von Ihrem addChangeListener und removeChangeListener zu entfernen. Sie sind bereits an Ihre Komponente gebunden, wenn sie angerufen werden.

    
PetersenDidIt 21.08.2015 01:02
quelle
0

Ich habe es so entschieden

%Vor%     
quelle
0

Dies ist ein es6 Problem. React.createClass bindet 'this' ordnungsgemäß für alle Funktionen, die in seinem Bereich definiert sind.

Für es6 musst du selbst etwas tun, um das richtige "Dies" zu binden. Calling bind (dies) erstellt jedoch jedes Mal eine neue Funktion, und die Rückgabe des Rückgabewerts an removeChangeListener stimmt nicht mit der Funktion überein, die an addChangeListener übergeben wurde, die durch einen früheren bind (this) -Aufruf erstellt wurde.

Ich sehe hier eine Lösung, wo bind (this) einmal für jede Funktion aufgerufen wird und der Rückgabewert gespeichert und später wieder verwendet wird. Das wird gut funktionieren. Eine beliebtere und etwas sauberere Lösung ist die Pfeilfunktion von es6.

%Vor%

Pfeilfunktionen erfassen das 'Dies' des umgebenden Kontexts, ohne jedes Mal neue Funktionen zu erstellen. Es ist für solche Sachen entworfen.

    
Shu 10.12.2015 04:41
quelle
0

Da Sie die Lösung hier bereits kennen gelernt haben, werde ich versuchen zu erklären, was passiert.
Gemäß dem ES5-Standard haben wir den folgenden Code geschrieben, um Listener hinzuzufügen und zu entfernen.

%Vor%

Im obigen Code ist die Speicherreferenz für die Callback-Funktion (dh: this._updateStore) gleich. RemoveChangeListener sucht nach einer Referenz und entfernt sie.

Da dem ES6-Standard standardmäßig die automatische Bindung this fehlt, müssen Sie this explizit an die Funktion binden.

Note: Bind method returns new reference for the callback. Weitere Informationen zu bind

finden Sie hier

Hier tritt das Problem auf. Wenn wir this._updateStore.bind(this) ausführen, gibt die bind-Methode eine neue Referenz für diese Funktion zurück. Die Referenz, die Sie als Argument an addChangeListener gesendet haben, ist also nicht dieselbe wie die in der removeChangeListener-Methode.

this._updateStore.bind(this) != this._updateStore.bind(this)

Lösung:
Es gibt zwei Möglichkeiten, dieses Problem zu lösen.
1. Speichern Sie den Ereignishandler (ie: this._updateStore) im Konstruktor als Elementvariable. (Ihre Lösung)
2. Erstellen Sie im Geschäft eine benutzerdefinierte ChangeListener-Funktion, die this für Sie bindet. (Quelle: hier )

Lösung 1:

%Vor%

Im obigen Code binden wir this an die _updateStore-Funktion und weisen diese einem Member innerhalb des Konstruktors zu. Später verwenden wir diesen Member, um den Change Listener hinzuzufügen und zu entfernen.

Lösung 2: In dieser Methode modifizieren wir BaseStore-Funktionalitäten. Die Idee ist, die Funktion addChangeListener im BaseStore zu modifizieren, um das zweite Argument this zu erhalten. Innerhalb dieser Funktion binden wir this an den Callback und speichern diesen Verweis, so dass wir beim Entfernen des Change Listeners diesen Verweis entfernen können.

Hier finden Sie den vollständigen Code und die Quelle hier .

    
Murali Manohar 23.05.2017 12:26
quelle

Tags und Links