Sollten Flussmittelspeicher oder Aktionen (oder beides) externe Dienste berühren?

122

Sollten die Geschäfte ihren eigenen Status beibehalten und dabei Netzwerk- und Datenspeicherdienste aufrufen können ... In diesem Fall handelt es sich bei den Aktionen nur um dumme Nachrichtenpassanten.

-ODER-

... sollten die Speicher dumme Empfänger unveränderlicher Daten aus den Aktionen sein (und die Aktionen sind diejenigen, die Daten zwischen externen Quellen abrufen / senden? Der Speicher würde in diesem Fall als Ansichtsmodelle fungieren und in der Lage sein, ihre zu aggregieren / zu filtern Daten, bevor sie ihre eigene Zustandsbasis auf die unveränderlichen Daten setzen, die sie von der Aktion erhalten haben.

Es scheint mir, dass es das eine oder andere sein sollte (und nicht eine Mischung aus beiden). Wenn ja, warum wird einer dem anderen vorgezogen / empfohlen?

plaxdan
quelle
2
Dieser Beitrag könnte helfen, code-experience.com/…
Markus-ipse
Für diejenigen, die die verschiedenen Implementierungen des Flussmusters evaluieren, würde ich einen Blick auf Redux empfehlen. Github.com/rackt/redux Stores werden als reine Funktionen implementiert, die den aktuellen Status annehmen und eine neue Version dieses Status ausgeben. Da es sich um reine Funktionen handelt, wird Ihnen die Frage, ob sie Netzwerk- und Speicherdienste aufrufen können oder nicht, aus den Händen genommen: Sie können nicht.
Plaxdan

Antworten:

151

Ich habe gesehen, dass das Flussmuster in beide Richtungen implementiert wurde, und nachdem ich beides selbst getan habe (zunächst mit dem früheren Ansatz), glaube ich, dass Speicher dumme Empfänger von Daten aus den Aktionen sein sollten und dass die asynchrone Verarbeitung von Schreibvorgängen in der Aktionsersteller. ( Asynchrone Lesevorgänge können unterschiedlich gehandhabt werden .) Nach meiner Erfahrung hat dies einige Vorteile in der Reihenfolge ihrer Wichtigkeit:

  1. Ihre Geschäfte werden vollständig synchron. Auf diese Weise ist Ihre Geschäftslogik viel einfacher zu befolgen und sehr einfach zu testen. Instanziieren Sie einfach ein Geschäft mit einem bestimmten Status, senden Sie ihm eine Aktion und prüfen Sie, ob sich der Status wie erwartet geändert hat. Darüber hinaus besteht eines der Kernkonzepte von Flux darin, kaskadierende Versendungen und mehrere Versendungen gleichzeitig zu verhindern. Dies ist sehr schwierig, wenn Ihre Filialen eine asynchrone Verarbeitung durchführen.

  2. Alle Aktionsversendungen erfolgen von den Aktionserstellern. Wenn Sie asynchrone Vorgänge in Ihren Filialen ausführen und die Aktionshandler Ihrer Filialen synchron halten möchten (und dies sollten Sie tun, um die Garantien für den Fluss-Einzelversand zu erhalten), müssen Ihre Filialen als Reaktion auf asynchrone Aktionen zusätzliche SUCCESS- und FAIL-Aktionen auslösen wird bearbeitet. Wenn Sie diese Versendungen stattdessen in die Aktionsersteller einfügen, können Sie die Jobs der Aktionsersteller und der Speicher trennen. Darüber hinaus müssen Sie nicht in Ihrer Geschäftslogik stöbern, um herauszufinden, woher die Aktionen gesendet werden. Eine typische asynchrone Aktion in diesem Fall könnte ungefähr so ​​aussehen (ändern Sie die Syntax der dispatchAufrufe basierend auf der Art des verwendeten Flusses):

    someActionCreator: function(userId) {
      // Dispatch an action now so that stores that want
      // to optimistically update their state can do so.
      dispatch("SOME_ACTION", {userId: userId});
    
      // This example uses promises, but you can use Node-style
      // callbacks or whatever you want for error handling.
      SomeDataAccessLayer.doSomething(userId)
      .then(function(newData) {
        // Stores that optimistically updated may not do anything
        // with a "SUCCESS" action, but you might e.g. stop showing
        // a loading indicator, etc.
        dispatch("SOME_ACTION_SUCCESS", {userId: userId, newData: newData});
      }, function(error) {
        // Stores can roll back by watching for the error case.
        dispatch("SOME_ACTION_FAIL", {userId: userId, error: error});
      });
    }

    Logik, die andernfalls über verschiedene Aktionen hinweg dupliziert werden könnte, sollte in ein separates Modul extrahiert werden. In diesem Beispiel wäre dies das Modul SomeDataAccessLayer, das die eigentliche Ajax-Anforderung ausführt.

  3. Sie benötigen weniger Action-Ersteller. Das ist weniger eine große Sache, aber schön zu haben. Wie in Nr. 2 erwähnt, müssen Sie zusätzliche Aktionen auslösen, um die Ergebnisse asynchroner Vorgänge zu verarbeiten, wenn Ihre Filialen über eine synchrone Versandabwicklung verfügen (und dies sollten). Durch das Ausführen der Versendungen in den Aktionserstellern kann ein einzelner Aktionsersteller alle drei Aktionstypen auslösen, indem er das Ergebnis des asynchronen Datenzugriffs selbst verarbeitet.

Michelle Tilley
quelle
15
Ich denke, was den Web-API-Aufruf (Action Creator vs. Store) auslöst, ist weniger wichtig als die Tatsache, dass der Erfolgs- / Fehler-Rückruf eine Aktion erstellen sollte. Der Datenfluss ist dann also immer: Aktion -> Dispatcher -> Speichern -> Ansichten.
Fisherwebdev
1
Wäre es besser / einfacher, die eigentliche Anforderungslogik in ein API-Modul zu integrieren? Ihr API-Modul könnte also nur ein Versprechen zurückgeben, von dem Sie versenden. Der Aktionsersteller sendet nur basierend auf Auflösung / Fehler, nachdem er eine erste ausstehende Aktion gesendet hat. Die Frage bleibt, wie die Komponente diese 'Ereignisse' abhört, da ich nicht sicher bin, ob der Anforderungsstatus dem Speicherstatus zugeordnet werden soll.
Backdesk
@backdesk Genau das mache ich im obigen Beispiel: Versende eine erste ausstehende Aktion ( "SOME_ACTION"), verwende eine API, um eine Anfrage ( SomeDataAccessLayer.doSomething(userId)) zu stellen, die ein Versprechen zurückgibt, und .thenversende in den beiden Funktionen zusätzliche Aktionen. Der Anforderungsstatus kann (mehr oder weniger) dem Speicherstatus zugeordnet werden, wenn die Anwendung den Status des Status kennen muss. Wie diese Karte der App entspricht (z. B. hat jeder Kommentar einen individuellen Fehlerstatus, a la Facebook, oder es gibt eine globale Fehlerkomponente)
Michelle Tilley
@MichelleTilley "Eines der Kernkonzepte von Flux besteht darin, kaskadierende Versendungen und mehrere Versendungen gleichzeitig zu verhindern. Dies ist sehr schwierig, wenn Ihre Filialen eine asynchrone Verarbeitung durchführen." Das ist ein wichtiger Punkt für mich. Gut gesagt.
51

Ich habe diese Frage an die Entwickler auf Facebook getwittert und die Antwort von Bill Fisher war:

Wenn ich auf die Interaktion eines Benutzers mit der Benutzeroberfläche reagiere, würde ich den asynchronen Aufruf in den Methoden zum Erstellen von Aktionen ausführen.

Wenn Sie jedoch einen Ticker oder einen anderen nicht menschlichen Fahrer haben, funktioniert ein Anruf aus dem Geschäft besser.

Wichtig ist, dass Sie im Fehler- / Erfolgsrückruf eine Aktion erstellen, damit die Daten immer aus Aktionen stammen

Markus-ipse
quelle
Während dies Sinn macht, eine Idee warum a call from store works better when action triggers from non-human driver ?
SharpCoder
@ SharpCoder Ich denke, wenn Sie einen Live-Ticker oder ähnliches haben, müssen Sie nicht wirklich eine Aktion auslösen, und wenn Sie dies aus dem Geschäft heraus tun, müssen Sie wahrscheinlich weniger Code schreiben, da das Geschäft sofort auf den Status zugreifen kann & eine Änderung ausgeben.
Florian Wendelborn
8

Die Geschäfte sollten alles tun, einschließlich des Abrufs von Daten und der Signalisierung an Komponenten, dass die Daten des Geschäfts aktualisiert wurden. Warum? Denn Aktionen können dann leicht, wegwerfbar und austauschbar sein, ohne das wichtige Verhalten zu beeinflussen. Alle wichtigen Verhaltensweisen und Funktionen finden im Geschäft statt. Dies verhindert auch das Duplizieren von Verhalten, das sonst in zwei sehr ähnlichen, aber unterschiedlichen Aktionen kopiert würde. Die Geschäfte sind Ihre einzige Quelle für den Umgang mit der Wahrheit.

In jeder Flux-Implementierung, die ich gesehen habe, sind Aktionen im Grunde genommen Ereigniszeichenfolgen, die in Objekte umgewandelt wurden, wie traditionell ein Ereignis mit dem Namen "anchor: clicked", aber in Flux wird es als AnchorActions.Clicked definiert. Sie sind sogar so "dumm", dass die meisten Implementierungen separate Dispatcher-Objekte haben, um die Ereignisse tatsächlich an die Geschäfte zu senden, die sie abhören.

Persönlich mag ich die Implementierung von Flux durch Reflux, bei der es keine separaten Dispatcher-Objekte gibt und Action-Objekte den Versand selbst durchführen.


Bearbeiten: Facebooks Flux holt tatsächlich "Action Creators" ein, damit sie intelligente Aktionen verwenden. Sie bereiten die Nutzlast auch über die Geschäfte vor:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/actions/ChatMessageActionCreators.js#L27 (Zeile 27 und 28)

Der Rückruf nach Abschluss würde diesmal eine neue Aktion mit den abgerufenen Daten als Nutzlast auslösen:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

Ich denke, das ist die bessere Lösung.

Rygu
quelle
Was ist diese Reflux-Implementierung? Ich habe noch nichts davon gehört. Ihre Antwort ist interessant. Sie meinen, dass Ihre Store-Implementierung die Logik haben sollte, API-Aufrufe usw. auszuführen? Ich dachte, die Geschäfte sollten nur Daten empfangen und ihre Werte aktualisieren. Sie filtern nach bestimmten Aktionen und aktualisieren einige Attribute ihrer Geschäfte.
Jeremy D
Reflux ist eine geringfügige Variation von Facebooks Flux: github.com/spoike/refluxjs Stores verwalten die gesamte "Modell" -Domäne Ihrer Anwendung im Vergleich zu Aktionen / Dispatchern, die nur Dinge zusammenfügen und zusammenkleben.
Rygu
1
Also habe ich noch etwas darüber nachgedacht und (fast) meine eigene Frage beantwortet. Ich hätte es hier als Antwort hinzugefügt (damit andere darüber abstimmen können), aber anscheinend bin ich im Stackoverflow zu karmaarm, um noch eine Antwort veröffentlichen zu können. Also hier ist ein Link: groups.google.com/d/msg/reactjs/PpsvVPvhBbc/BZoG-bFeOwoJ
plaxdan
Vielen Dank für den Google-Gruppenlink, er scheint wirklich informativ zu sein. Ich bin auch mehr Fan von allem, was durch den Dispatcher geht, und einer wirklich einfachen Logik im Laden, im Grunde genommen, ihre Daten zu aktualisieren, das ist alles. @ Rygu Ich werde Reflux überprüfen.
Jeremy D
Ich habe meine Antwort mit einer alternativen Ansicht bearbeitet. Scheint, dass beide Lösungen möglich sind. Ich würde mit ziemlicher Sicherheit die Lösung von Facebook anderen vorziehen.
Rygu
3

Ich werde ein Argument für "dumme" Aktionen vorbringen.

Indem Sie die Verantwortung für das Sammeln von Ansichtsdaten in Ihre Aktionen übernehmen, koppeln Sie Ihre Aktionen mit den Datenanforderungen Ihrer Ansichten.

Im Gegensatz dazu ermöglichen generische Aktionen, die die Absicht des Benutzers oder einen Statusübergang in Ihrer Anwendung deklarativ beschreiben , jedem Geschäft, das auf diese Aktion reagiert, die Absicht in einen Status umzuwandeln, der speziell auf die abonnierten Ansichten zugeschnitten ist.

Dies bietet sich für zahlreichere, aber kleinere, spezialisiertere Geschäfte an. Ich argumentiere für diesen Stil, weil

  • Dies gibt Ihnen mehr Flexibilität bei der Verwendung von Speicherdaten durch Ansichten
  • "intelligente" Speicher, die auf die Ansichten spezialisiert sind, die sie verwenden, sind für komplexe Apps kleiner und weniger gekoppelt als "intelligente" Aktionen, von denen möglicherweise viele Ansichten abhängen

Der Zweck eines Geschäfts besteht darin, Daten für Ansichten bereitzustellen. Der Name "Aktion" deutet darauf hin, dass der Zweck darin besteht, eine Änderung in meiner Anwendung zu beschreiben.

Angenommen, Sie müssen einer vorhandenen Dashboard-Ansicht ein Widget hinzufügen, das einige ausgefallene neue aggregierte Daten anzeigt, die Ihr Backend-Team gerade eingeführt hat.

Bei "intelligenten" Aktionen müssen Sie möglicherweise Ihre "Aktualisierungs-Dashboard" -Aktion ändern, um die neue API zu verwenden. "Das Dashboard aktualisieren" im abstrakten Sinne hat sich jedoch nicht geändert. Die Datenanforderungen Ihrer Ansichten haben sich geändert.

Mit "dummen" Aktionen können Sie einen neuen Speicher hinzufügen, den das neue Widget verwenden soll, und ihn so einrichten, dass er beim Empfang des Aktionstyps "Aktualisierungs-Dashboard" eine Anforderung für die neuen Daten sendet und diese verfügbar macht das neue Widget, sobald es fertig ist. Für mich ist es sinnvoll, dass, wenn die Ansichtsebene mehr oder andere Daten benötigt, die Dinge, die ich ändere, die Quellen dieser Daten sind: Speicher.

Carlos Lalimarmo
quelle
2

Gaerons Flux- React -Router-Demo bietet eine nette Utility-Variante des 'richtigen' Ansatzes.

Ein ActionCreator generiert ein Versprechen von einem externen API-Dienst und übergibt das Versprechen und drei Aktionskonstanten an eine dispatchAsyncFunktion in einem Proxy / erweiterten Dispatcher. dispatchAsyncwird immer die erste Aktion auslösen, z. B. 'GET_EXTERNAL_DATA', und sobald das Versprechen zurückgegeben wird, wird entweder 'GET_EXTERNAL_DATA_SUCCESS' oder 'GET_EXTERNAL_DATA_ERROR' gesendet.

William Myers
quelle
1

Wenn Sie eines Tages eine Entwicklungsumgebung haben möchten, die mit der in Bret Victors berühmtem Video " Inventing on Principle" vergleichbaren vergleichbar ist , sollten Sie lieber dumme Speicher verwenden, die nur eine Projektion von Aktionen / Ereignissen innerhalb einer Datenstruktur ohne Nebenwirkungen sind. Es wäre auch hilfreich, wenn Ihre Filialen tatsächlich Mitglied derselben globalen unveränderlichen Datenstruktur wären, wie in Redux .

Weitere Erklärungen finden Sie hier: https://stackoverflow.com/a/31388262/82609

Sebastien Lorber
quelle