Reagiere die selbst-modifizierende Komponente mit dem richtigen Status / Requisiten

8

Ich arbeite gerade an einem Tierprojekt und habe mich entschieden, React für die Ansicht zu verwenden. Ich bin ziemlich weit in der Entwicklung, aber ich beginne gerade zu erkennen, dass ich nicht verstehen kann, wie ich React sehr gut gebrauchen kann, weil es so aussieht, als müsste man, um einfache Dinge zu tun, sehr extreme Maßnahmen ergreifen!

>

Also, Zeit für ein beliebiges Beispiel! Hier ist der Code, der auf JsFiddle

funktioniert %Vor%

Die Idee ist, dass ich versuche, eine Komponente zu erstellen, die sich selbst modifizieren kann. In diesem Beispiel kann die Änderung nur bewirken, dass eine Komponente ihren Namen von ihrem ursprünglichen Wert in "John" ändern kann.

Warum ist das so kompliziert?

Es scheint so, als würde dies viel einfacher gemacht werden, wenn Sie nur einen Zustand in den Name-Komponenten festlegen und sich selbst modifizieren lassen. Leider kann ich, soweit ich das beurteilen kann, den ursprünglichen Zustand basierend auf Requisiten einstellen, der gegen die "Requisiten in getInitialState ist ein Anti-Pattern" -Ding verstößt.

Warum machen Sie die Komponenten nicht einfach manuell?

In meiner realen Anwendung kommen die Daten vom Server, also müssen alle Daten auf einmal abgerufen werden. Aus diesem Grund habe ich getInitialState verwendet, um die Daten vorzuladen, anstatt einfach mehrere Name-Komponenten manuell zu erstellen. Außerdem wird in den React-Dokumenten angegeben, dass der Status in einer gemeinsamen übergeordneten Komponente gespeichert werden soll. Daher folgt dieser Code diesem Muster.

Die Dinge beginnen komplex zu werden

Die Probleme beginnen, wenn Sie diesen Status ändern müssen. Da es sich um ein Array von Objekten handelt (sehr typisch für die Serialisierung von Daten vom Server), können Sie nicht einfach sofort das spezifische Objekt auswählen, das der Komponente entspricht, mit der Sie zu tun haben. Soweit ich das beurteilen kann, besteht die einzige Möglichkeit darin, eine ID an alle untergeordneten Elemente zu übergeben, damit sie diese zurückgeben können, wenn Event-Handler ausgelöst werden, um sich selbst zu identifizieren. Aber selbst dann müssen Sie immer noch jedes Element in diesem Zustand durchlaufen, um dasjenige zu finden, das Sie tatsächlich ändern möchten.

Die Komplexität verstärkt sich ...

Schlimmer noch, es scheint, dass Sie nicht auf die Schlüsseleigenschaft innerhalb einer Komponente zugreifen können, für die diese Eigenschaft deklariert ist. Um also alle benötigten Informationen zu haben, müssen Sie diesen Wert (Schlüssel und ID in meinem Beispiel). Dies ist, wo die roten Fahnen wirklich für mich gingen, da es scheint, als wäre die einzige Möglichkeit, um das Ziel zu erreichen, während kein Muster zu brechen ist, einen Teil ihres Systems zu umgehen.

Eine weitere Sache, über die ich mich ärgere, ist, dass Sie einen Event-Handler vom statushaltenden Objekt zum untergeordneten Objekt übergeben müssen. In diesem Beispiel ist das ziemlich einfach, aber in meiner tatsächlichen Anwendung ist diese Beziehung vier Ebenen tief . Es scheint mir, dass all diese Zwischenschritte die Wahrscheinlichkeit erhöhen, Bugs einzuführen, indem sie einen Schritt in der Kette der Requisiten verpassen.

Was ist die Antwort?

Ist React so? Gibt es eine massive Abkürzung, die ich komplett ignoriere? Wie kann dies einfacher gemacht werden, während ich immer noch die von mir spezifizierten Einschränkungen einhalte?

    
Trappar 13.06.2015, 09:47
quelle

2 Antworten

5

Es gibt, glaube ich, ein paar Probleme mit Ihrem Beispiel, die es komplexer als nötig machen: Die Name-Komponente ist eine undichte Abstraktion, und die Name-Komponente weiß zu viel. (Diese sind eigentlich ein wenig miteinander verflochten.)

Bevor ich auf diese Fragen eingehe, möchte ich einen kleinen Umweg machen, um meine Vorgehensweise beim Entwurf von Bauteilen zu diskutieren. Ich finde, dass das Mischen von Top-Down- und Bottom-Up-Ansätzen beim Erstellen von React-Anwendungen sehr nützlich ist. Mithilfe von Top-down können Sie ermitteln, welche Komponenten erstellt werden, indem Sie mit der Idee einer Komponente beginnen und ermitteln, was ihre Unterkomponenten sein sollen, und dann die Unterkomponenten der Unterkomponenten ad nauseam identifizieren. Nach der Identifizierung der Komponenten ist es an der Zeit, zu einem Bottom-up-Ansatz zu wechseln, bei dem Sie die grundlegendste Komponente (keine neuen Unterkomponenten zum Erstellen) verwenden und sie so eigenständig und unabhängig wie möglich erstellen und dann die nächste Komponente auswählen Basiskomponente und implementieren Sie es, und wiederholen Sie, bis Sie fertig sind.

Beim Implementieren einer Komponente können Sie sich die eingehenden Requisiten als API für andere Komponenten vorstellen, und Sie sollten so konzentriert wie möglich auf die Funktionalität Ihrer Komponente achten.

Also zurück zu den Problemen in Ihrem Beispiel.

Das erste ist, dass Ihre Name-Komponente ein bisschen eine undichte Abstraktion ist. (Ja, ich weiß, dies ist nur ein willkürliches Beispiel, aber ertragen Sie mit mir.) Die Komponente präsentiert onClick als Teil seiner API (da es als Prop benötigt wird). Das ist problematisch, weil es Komponenten, die es verwenden wollen, sagt: "Hey, ich habe hier einen Knopf!" Das ist gut mit Ihrer aktuellen Implementierung von Name, aber was passiert, wenn Sie Name ändern müssen, um anderes Verhalten zu haben? onClick macht möglicherweise keinen Sinn mehr, und wenn Sie diesen Propnamen ändern möchten, dann wird diese Änderung im gesamten restlichen Code kräuseln. Stattdessen sollten Sie den Namen 'update' verwenden, der für eine Name-Komponente eine sinnvolle Operation zu sein scheint, während die Tatsache, dass sie intern eine Schaltfläche enthält, ausgeblendet wird. Sicher, dies kann als ein "Sei vorsichtig, wenn man Dinge nennt" -Argument betrachtet werden, aber ich denke, eine gute Benennung ist ein wertvolles Werkzeug beim Erstellen von Komponenten, die sowohl einfach zu verwenden als auch später leicht zu modifizieren sind.

Das andere Problem ist, dass die Name-Komponente zu viel über die übergeordnete Implementierung weiß, was es später schwieriger macht, sie in anderen Komponenten zu verwenden. Anstatt "bind" in der Name-Komponente auszuführen, nehmen wir an, dass der besitzende Container eine Funktion mit einem einzigen Parameter - newName - übergibt. Innerhalb der Namenskomponente müssen Sie diese Funktion dann nur mit dem neuen Namen aufrufen. Wie sich dies auf einen anderen Status auswirkt, ist nicht das Problem von Name, und Sie haben dieses Problem effektiv zurückgestellt, bis Sie keine andere Wahl haben. Die Name-Komponente sieht dann folgendermaßen aus:

%Vor%

Beachten Sie, dass Sie die ID nicht mehr benötigen; Sie akzeptieren und aktualisieren nur einen Namen.

Da Sie sich eigentlich Sorgen machen müssen, die Namen in der NameContainer-Komponente zu aktualisieren (da sich die Daten dort befinden), können Sie sich darüber Gedanken machen. Um ein Schleifen aller Ids zu vermeiden, erstellen wir eine Funktion, die ein Namensobjekt und einen neuen Namen entgegennimmt und dann jedes Namensobjekt an die Funktion bindet, ähnlich wie in Ihrer Namenskomponente. (Was übrig bleibt, ist eine Funktion, die einen einzelnen Parameter newName akzeptiert, den wir als Aktualisierungsfunktion für Name verwenden können!). Der Code sieht folgendermaßen aus:

%Vor%

Dies vereinfacht Ihre Komponenten wirklich und jetzt kann Name in einer größeren Vielfalt von Kontexten verwendet werden. Ich habe Ihr Beispiel mit den hier beschriebenen Änderungen aktualisiert, und ich habe für Tritte eine andere Implementierung von Name hier . Der NameContainer musste nicht geändert werden, da sich die Name-API nicht geändert hat obwohl die Implementierung von Name ziemlich unterschiedlich ist.

Wenn Sie diesen Bottom-up-Ansatz verwenden, können Sie damit umgehen, dass Sie Requisiten durch eine Kette von Unterkomponenten leiten. Wenn Sie eine Komponente erstellen und testen, müssen Sie sich nur mit dem sehr spezifischen Problem befassen, das Sie mit dieser Komponente lösen. Stellen Sie sicher, dass Sie geeignete Requisiten an seine Unterkomponenten übergeben. Da Sie sich auf ein sehr spezifisches Problem konzentrieren, verpassen Sie es wahrscheinlich weniger, wichtige Daten zu übermitteln.

Sie können sich das ansehen und fragen sich immer noch: "Warum ist das so kompliziert?". Stellen Sie sich eine Anwendung vor, bei der dieselben Daten gleichzeitig auf verschiedene Arten angezeigt werden sollen (z. B. ein Diagramm und die entsprechende Datentabelle). Mit React können Sie Daten für verschiedene Komponenten freigeben und synchronisieren. Wenn Sie alle Daten und so lokal wie möglich manipuliert haben (so weit wie möglich in den Unterkomponentenketten), dann müssten Sie die Synchronisation zwischen den Komponenten selbst implementieren.Indem Sie die Daten so hoch wie möglich in der Komponentenkette platzieren, machen Sie diese Komponenten (effektiv) global, und React kann sich um die restlichen Details kümmern. Sie sorgen sich darum, Daten zu übergeben und die Daten zu rendern, und React kümmert sich um den Rest.

Als letzte Anmerkung sollten Sie, wenn Ihre Anwendung immer komplexer wird, einen Blick auf Flux . Es ist ein Architekturmuster, mit dem Sie die Komplexität Ihrer Anwendung verwalten können. Es ist kein Allheilmittel, aber ich fand es ziemlich gut, wenn ich es in einigermaßen komplexen Apps verwendete.

    
Chad Taylor 13.06.2015, 17:47
quelle
1

Es sieht so aus, dass Sie in Ihrem Fall Requisiten verwenden, um einen Anfangswert zu übergeben. Dann verwenden Sie eine Schaltfläche, um diesen Wert zu ändern. Dieser Anwendungsfall fällt nicht in das "Anti-Pattern" -Prinzip, um den Status zu setzen.

Aus Reacts Dokumentation :

  

Es ist jedoch kein Anti-Pattern, wenn Sie klarstellen, dass Synchronisation hier nicht das Ziel ist

In dem sie das Beispiel geben:

%Vor%

Also denke ich, dass Sie in diesem Fall einfach den Anfangswert Ihrer Name-Komponente verwenden, diesen Wert in getInitialState setzen und dann die Schaltfläche this.setState aufrufen, um sie in etwas zu ändern sonst.

Ich glaube, dass diese Lösung die meisten Fragen beantwortet, die Sie gestellt haben.

Sie haben die richtige Idee, wenn Sie die übergeordnete Komponente zum Abrufen der Informationen auf dem Server verwenden, um herauszufinden, wie viele Name-Komponenten erstellt werden sollen. Nachdem dieser Prozess abgeschlossen ist, verwenden Sie map , um alle Namen-Komponenten zu kompilieren, die Sie erstellen möchten, und render() , um sie von dort zu übernehmen. Das ist alles genau so, wie ich es machen würde. Bedenken Sie jedoch, dass mit der Verwendung von Requisiten zur Angabe eines Standardwerts für eine Komponente und der Angabe des aktuellen Werts für die Komponente nichts falsch ist.

    
Michael Parker 13.06.2015 16:16
quelle

Tags und Links