Redux: Beispiele - Wie implementiere ich den Namespace für Aktionstypen?

Erstellt am 23. Sept. 2015  ·  32Kommentare  ·  Quelle: reduxjs/redux

Ich habe jetzt ungefähr eine Woche damit verbracht, nach guten Implementierungen des Namespaces für Aktionstypen zu suchen, und habe meine eigene Version erstellt, die auf Übergangsklassen basiert, die die Definition eines Aktionserstellers, -reduzierers und eine Liste seiner untergeordneten Übergänge enthalten. Dieser Ansatz erfordert jedoch einen Weg zu viel Kesselplattencode und es ist problematisch, wenn Sie viele sehr einfache Aktionen haben.

In allen Beispielen, die Sie in diesem Repo und vielen anderen finden, wird dieses Problem aus irgendeinem Grund immer ignoriert, aber dies war das erste Problem, das ich entdeckt habe, als ich anfing, mich mit Redux zu befassen.

Vermisse ich hier etwas? Ihr habt keine Kollisionen beim Benennen von Aktionstypen?

Kann jemand die Beispiele aktualisieren und mir zeigen, wie Sie mit diesem Problem umgehen, oder mich in die richtige Richtung weisen?

Mein spezieller Fall bezieht sich auf 2 separate Admin-Panels im Frontend. Eine für Tester und eine für Kunden, deren Status in separaten Abschnitten des Geschäfts gespeichert ist ("testerAccount", "customerAccount"), aber beide können ähnliche Aktionen ausführen, z. B. ADD_VIDEO_UPLOAD, ADD_COMMENT usw. usw.

Ich würde mich sehr über Ihre Hilfe hier freuen :)

question

Hilfreichster Kommentar

Zeichenfolgen sind für den Namespace nicht von Natur aus schlecht. URLs sind Zeichenfolgen und scheinen gut zu funktionieren.

Alle 32 Kommentare

AccountType in Aktionen umsetzen? Siehe auch examples/real-world/reducers wie Sie eine Reduzierungsfabrik schreiben und denselben Code mehrmals verwenden können, um Reduzierungen zu erstellen, die auf verschiedene Aktionen reagieren.

Vergessen Sie auch nicht, dass Aktionsersteller nur Funktionen sind, und Sie können Funktionen erstellen, die eine Reihe anderer Funktionen zurückgeben.

Ich denke, es wäre wirklich hilfreich, wenn Sie ein bestimmtes kleines Beispiel für das Problem angeben würden, von dem Sie sagen, dass es in vorhandenen Beispielen ignoriert wird.

Wir können Ihnen dann helfen, Wege zu finden, um dieses spezielle Beispiel zu vereinfachen. Es ist sehr schwer, als Antwort auf eine Frage ohne Code etwas Bestimmtes vorzuschlagen.

@gaearon Ich weiß, wohin Sie damit gehen, aber ich versuche nicht, wiederverwendbare Reduzierungen, Action-Ersteller oder TROCKENE Dinge zu erstellen, ganz im Gegenteil.

Ich versuche, Gruppen von Aktionen voneinander zu isolieren, damit zwei Personen an zwei verschiedenen Abschnitten des Systems arbeiten können und ich sicher bin, dass sie nicht versehentlich die Reduzierungen der anderen auslösen, da die Namen der Aktionstypen keinen Namespace enthalten.

Hoffe das macht es klarer.

Ich habe dieses Problem in meinen Projekten mithilfe von https://www.npmjs.com/package/flux-constant in Kombination mit einer supereinfachen Hilfsfunktion namens createActionTypes gelöst.

Im Wesentlichen lautet mein Code:

// create-action-types.js
var fluxConstant = require('flux-constant');
module.exports = function (types) {
    return fluxConstant.set(types);
};

// in foo-action-types.js
module.exports = createAtionTypes([
    'ADD_FOO',
    'REMOVE_FOO'
]);

// in some-store.js
var fooActionTypes = require('foo-action-types');
function (state, action) {
    switch(action.type) {
        case fooActionTypes.ADD_FOO: 

        case fooActionTypes.REMOVE_FOO:
   }
}

Warum nicht alle Aktionstypen als Konstanten an einem Ort aufbewahren?
Dann können sie sicher nicht zusammenstoßen, weil Sie denselben Namen nicht zweimal exportieren können.

Nehmen wir an, dies ist der Anfangszustand des Geschäfts, um dies besser zu veranschaulichen:


let initialState = {
  "testerAccount": {
    "videoUploads": [],
    "messages": []
  },
  "customerAccount": {
    "videoUploads": [],
    "messages": []
  },
  "systemUserAccount": {
    "videoUploads": [],
    "messages": []
  }
};

Wie vermeiden Sie Kollisionen von Namen von Aktionstypen, wenn Sie separate Reduzierungen zum Hinzufügen von Videos oder Nachrichten für jeden dieser Abschnitte implementieren?

@gaearon, was Sie vorschlagen, löst nicht den Kern des Problems, sondern eher den Klebebandansatz, da Sie im Laufe der Zeit eine riesige Typdatei erhalten, die viele Probleme verursachen wird.

Es wird sofort Probleme beim Zusammenführen von Code verursachen, die später mit Namenshacks gelöst werden müssen, z. B.:

ADD_VIDEO_UPLOAD, ADD_TESTER_VIDEO_UPLOAD, ADD_VIDEO_UPLOAD_IN_SOME_SECTION usw.

Das ist wieder ein großer Kopfschmerz.

@koulmomo Genau das habe ich gesucht, danke :): +1: Einfach und mächtig.

@gaearon Ich denke, wir sollten mindestens ein Beispiel haben, das entweder dieses Paket oder ein neues Paket verwendet, das als Redux-Konstante bezeichnet werden könnte.

Meiner Meinung nach sollte die Förderung des Ansatzes in den Dokumenten / Beispielen, der das Problem des Namespaces löst, die neue Standardeinstellung sein.

Vielen Dank für Ihre Hilfe dabei :)

In Ihrem Beispiel verstehe ich nicht wirklich, warum nicht ein Satz von Typen, ein Reduzierungsgenerator und ein Satz von Aktionserstellern vorhanden sind. Die Aktionen würden die Eigenschaft accountType , um sie voneinander zu unterscheiden. Aktionsersteller würden dies als Parameter akzeptieren. Die Reduziererfabrik würde accountType akzeptieren und einen Reduzierer zurückgeben, der nur Aktionen mit diesem Kontotyp verarbeitet.

Ich denke nicht, dass flux-constant hier eine großartige Lösung ist. Es scheint sich auf instanceof Schecks zu stützen. Das heißt, Sie können Ihre Aktionen nicht serialisieren und später erneut abspielen, da - bam! - ihre deserialisierten Typen nicht mit den generierten übereinstimmen.

Menschen versuchen oft, Flux „einfacher“ zu machen, ohne zu bemerken, dass sie seine wesentlichen Merkmale brechen .

In Ihrem Beispiel verstehe ich nicht wirklich, warum nicht ein Satz von Typen, ein Reduzierungsgenerator und ein Satz von Aktionserstellern vorhanden sind. Die Aktionen würden die accountType-Eigenschaft enthalten, um sie voneinander zu unterscheiden. Aktionsersteller würden dies als Parameter akzeptieren. Die Reduziererfactory akzeptiert accountType und gibt einen Reduzierer zurück, der nur Aktionen mit diesem Kontotyp verarbeitet.

Die offensichtliche Symmetrie in Ihrem Beispiel hat mich irregeführt. Ich verstehe jetzt, dass Sie meinten, dass es hier kein TROCKEN gibt, und die Symmetrie der Funktionen ist nur offensichtlich, wie Sie unter https://github.com/rackt/redux/issues/786#issuecomment -142649749 gesagt haben.

Ich glaube nicht, dass hier eine Bibliothek benötigt wird. Richten Sie eine Konvention ein! Wenn Ihre Teilprojekte oder Funktionen beispielsweise so getrennt sind, sollten Sie die Aktionstypen feature/ACTION_TYPE aufrufen, z. B. testers/UPLOAD_VIDEO oder customers/UPLOAD_VIDEO . Dies ist besonders schön, wenn diese „Gruppen“ tatsächlich echten Ordnern (oder besser Paketen) auf der Festplatte entsprechen.

Verstöße sind in Codeüberprüfungen leicht zu erkennen. Wenn Sie wirklich wollen, können Sie dies automatisieren, aber ich kann nicht sehen, was es über manuellen Namespace bringt. In jedem Modul möchten Sie weiterhin alle Konstanten in einer Datei deklarieren, um die Kontrolle über Funktionen zu vereinfachen, versehentliche Doppelarbeit zu vermeiden und als Dokumentation.

Ich bin alle dafür, ein huge-apps -Beispiel mit Codeaufteilung, Aktionstypen mit Namespace usw. hinzuzufügen. Dies wäre jedoch ein separates Problem.

Zeichenfolgen sind für den Namespace nicht von Natur aus schlecht. URLs sind Zeichenfolgen und scheinen gut zu funktionieren.

@gaearon Sie haben Recht, ich habe das Problem der Serialisierung mit der auf Flusskonstanten basierenden Lösung vergessen.

Ich habe kein Problem mit stringbasierten Namespaces, solange wir die Module / Namespaces im Namen selbst leicht unterscheiden können.

Die auf Namenskonventionen basierende Lösung habe ich bereits gesehen:

https://github.com/erikras/ducks-modular-redux

aber ich hoffte, dass es einen besseren oder "richtigen" Weg geben könnte, der auf Ihren Erfahrungen basiert.

Ich denke, ich werde versuchen, den Flusskonstanten-Ansatz in etwas umzuwandeln, das Sie tatsächlich auf irgendeine Weise serialisieren könnten.

Bitte fügen Sie ein Beispiel für "Riesen-Apps" hinzu, wenn Sie Zeit dafür finden, da dies anderen Menschen viel Zeit spart und möglicherweise zu einer Art Konvention führt, der wir später leicht folgen können.

Danke nochmal :)

Ja, tut mir leid, leider habe ich seit einiger Zeit nicht mehr an einer großen App gearbeitet, und selbst als ich das tat, fand ich es wirklich gut, dass wir eine einzige riesige Datei mit Konstanten haben, die in Abschnitte gruppiert sind, weil sie einen guten Überblick darüber bietet, was passieren kann in der App.

Ich würde gerne sehen, wie dieses "Problem" in den Dokumenten ausführlicher erklärt wird. Ich habe / hatte die gleichen Fragen wie @pbc. Ich denke nicht "Warum nicht alle Aktionstypen als Konstanten an einem Ort behalten?" funktioniert, wenn Sie mehrere Module projektübergreifend wiederverwenden und die Empfehlung "Festlegen einer Konvention!" klingt sehr nach BEM im CSS-Land. Wir wechseln jedoch von BEM zu CSS-Modulen, und ich denke, etwas Ähnliches wie CSS-Module, bei dem Hash-Klassennamen auch für Aktionstypen in großen Projekten benötigt werden.

Soweit ich das beurteilen kann, gibt es keine Möglichkeit, sowohl Serialisierung als auch Abwesenheit von Konflikten zu erreichen.

Eine gute Konvention für wiederverwendbare Module könnte darin bestehen, dass bereits vorhandene "Eindeutigkeitsanbieter" wie Reverse-Domain-Namen oder Benutzername / Repo auf Github oder registrierter Modulname auf npm verwendet werden.


BEARBEITEN: CSS-Module definieren dazu eine benutzerdefinierte Sprache über CSS und stellen den Klassennamen während der Vorverarbeitung ein Präfix voran (es kommt ohnehin auf Konventionen an, aber auf eine generierte).

+1 für die Frage und die Diskussion. Ich hatte auch viel mit diesem genauen Problem als @pbc zu kämpfen . Verwirrung für mich ist, dass wir CombineReducers für Reducer haben, was einen schönen Zustandsbaum ergibt, aber Aktionen hingegen fühlen sich mehr oder weniger wie ein globaler an. Wie es aussieht, muss jede Art von Namespace manuell durchgeführt werden.

Ich glaube, ich habe eine Lösung für serialisierbare String-Aktionstypen gefunden. Ich denke, es ist eine dieser Situationen, in denen unser Geist etwas künstlich einschränkt.

Die Grundidee ist, dass die Typkonstanten in keiner Weise mit dem Zeichenfolgenwert verknüpft sein müssen. Sie können also zufällig generierte Werte, Hash-Dateipfade oder alles andere verwenden, was für den Wert der konstanten Zeichenfolge des Typs eindeutig ist. In Ihrem Reduzierer importieren Sie die Typkonstante nach Namen und vergleichen sie nach Namen. Dieser Name kann von anderen Aktionen und in anderen Reduzierungen verwendet werden, spielt jedoch keine Rolle, da der Wert nicht identisch ist.

Beispiel hier: https://gist.github.com/samsch/63a54e868d7fa2b6023a

Das ist sinnvoll. Sie möchten immer noch einen von Menschen lesbaren Teil im Aktionsnamen behalten, aber das Generieren eindeutiger Präfixe funktioniert vollständig.

Wenn Sie beweisen können, dass sie einzigartig sind, natürlich.
Der beste Weg ist immer noch, bereits vorhandene Eindeutigkeitsanbieter zu verwenden: Reverse Domain-Namen oder npm-Modulnamen.

Namespacing mit Konvention hat seine Gefahren, wenn Sie Module schreiben, die an verschiedenen Orten verwendet werden können, die außerhalb Ihrer Kontrolle liegen.

Nehmen wir an, Sie haben ein Modul "Geometrie" mit dem Aktionstyp "Bereich". Dieses Modul wird an zwei Stellen verwendet:

  1. AppA-> Zeichnung-> Geometrie (Namespace = "Zeichnung / Geometrie / Fläche")
  2. AppB-> Trigonometrie-> Form-> Geometrie (Namespace = "Trigonometrie / Form / Fläche")

Jetzt haben Sie zwei widersprüchliche Namespaces, je nachdem, wo Ihr Modul verwendet wird.

  • Es ist keine gute Idee, diesen vollständigen Pfad in Ihrem Geometriemodul für ActionType "Area" fest zu codieren.
  • Halten Sie den Namen einfach: "Bereich".
  • Erstellen Sie den Namespace ähnlich wie bei der Reduziererzusammensetzung, indem Sie jedem übergeordneten Element ein Präfix hinzufügen.

Ich experimentiere mit folgendem Muster:

Erstellen Sie ein Verzeichnis für jeden Sammlungstyp, der Folgendes enthält:

  • consts
  • Reduzierstück
  • Komponenten
  • Sagen

Erstellen Sie Konstanten im folgenden Format:

// song-store/song-store-consts.js
export const ADD = 'SONG_STORE.ADD'
export const REMOVE = 'SONG_STORE.REMOVE'

Wenn Sie Konstanten in Reduzierungen, Saga oder Aktionen verwenden, import alle mit * :

// song-store/song-store-actions.js
import * as SONG_STORE from './song-store-consts'

export function addSongStore(name) {
  return {
    type: SONG_STORE.ADD,
    name
  }
}

export function removeSongStore(songStoreId) {
  return {
    type: SONG_STORE.REMOVE,
    songStoreId
  }
}

Leider nicht gut zum Baumschütteln. Wäre schön wenn ES6 erlaubt wäre:

import { ADD, REMOVE } as SONG_STORE from './song-store-actions'

Weiß jemand, ob Webpack 2 import * intelligent baumschütteln kann, sodass es keinen Code für Exporte bündelt, die nicht verwendet werden, selbst wenn sie mit * importiert werden?

Dies scheint ein sehr häufiges Problem zu sein und taucht auch häufig in unserem Büro auf:

  • Namenskonflikte verursachen unerwünschtes Verhalten.
  • Beschwerde über die Kesselplatte, die mit der Erstellung eindeutiger Aktionstypen verbunden ist.

Wir haben ein paar verschiedene Lösungen ausprobiert, aber nichts schien zu bleiben:

  1. Wenn Sie alle Aktionstypen als Konstanten an einem Ort behalten, ist die Verwaltung sicherlich einfacher, aber ich fand, dass sie etwas unhandlich wird, wenn die App wächst.
  2. Zufällig generierte Werte, die von @samsch erwähnt wurden, haben meine Aufmerksamkeit auf sich gezogen und es funktioniert auf jeden
  3. Der obige Kommentar von @philholden hat mich wirklich funktionsgesteuerte Architektur verwenden und die Verzeichnisstruktur ziemlich beschreibend ist.

Nachdem wir in den letzten Monaten experimentiert hatten, entschieden wir uns, die Verzeichnisstruktur zu verwenden, um unsere Aktionstypen zu benennen. Das manuelle Eingeben wird sehr schnell alt, daher habe ich versucht, etwas mit __filename zu tun, aber dies funktioniert aufgrund von Code-Bündelung usw. nicht. Dann habe ich mein erstes Babel-Plugin erstellt, das das Schlüsselwort __filenamespace transformiert

Beispiel

In App/testerAccount/index.js :

// Something like this
const ADD_VIDEO_UPLOAD = `${__filenamespace}/ADD_VIDEO_UPLOAD`;
const ADD_COMMENT = `${__filenamespace}/ADD_COMMENT`;

// Will be transformed into something like this
const ADD_VIDEO_UPLOAD = 'App/testerAccount/ADD_VIDEO_UPLOAD';
const ADD_COMMENT = 'App/testerAccount/ADD_COMMENT';

Probieren Sie es einfach aus, ich hoffe, es ist nützlich. Es wäre fantastisch, Feedback zu erhalten, insbesondere von @pbc oder @gaearon als Issue Creator und Library Creator.

https://www.npmjs.com/package/babel-plugin-filenamespace

Ich hatte wirklich gehofft, jemand würde einen "Standard" anbieten. Wir brauchen dafür keine Symbole oder Objekte, wir brauchen nur eine Konvention.

Werden wir "featureName$actionType" oder "fileName/ACTION_TYPE" oder "PROJECT.FEATURE.ACTION" ? Wenn wir uns alle auf etwas einigen können, wird es einfacher sein, Reduzierungen zu teilen.

Ich denke, mit dem Mangel an anderen verfügbaren Konventionen ist Ducks zum Defacto-Standard geworden.

const ACTION = 'app/feature/ACTION'; ist ziemlich ausreichend.

@gaearon hat immer erwähnt, dass eine 1: 1-Beziehung zwischen Aktionen und Reduzierungen wahrscheinlich eine schlechte Idee ist, und ich stimme dem wirklich zu. Aber was ist mit dem Fall, in dem zwei verschiedene Ansichten tatsächlich denselben Reduzierer aufrufen möchten?
Zum Beispiel. Ein einfacher Umschaltvorgang kann an zwei verschiedenen Stellen aktiviert oder deaktiviert werden:
/ myaccount / toggleNotification
/ dashboard / toggleNotification
Also sollten wir zwei Reduzierer mit dem gleichen Inhalt in haben
Reduzierungen / Benachrichtigungen.js
cc: @ samit4me , @philholden


Nur bei einem anderen Gedanken halte ich dies für eine gute Idee, zwei oder mehr Reduzierungen in einer Datei zu haben, die denselben Job mit unterschiedlichen Aktionsnamen ausführen. Auf diese Weise können wir anhand des Reduzierers verstehen, dass an wie vielen verschiedenen Stellen ein bestimmter Zustand geändert wird.

Wie auch immer, würde gerne von den Experten hören. Vielen Dank.

@ivks : einige Gedanken hier.

Erstens können Sie dieselbe Aktion von mehreren Stellen in der Anwendung aus senden.

Zweitens können Sie definitiv denselben Reduzierer haben, der auf mehrere Aktionstypen wartet und diese auf dieselbe Weise behandelt.

Drittens geht es beim Aspekt "1: 1-Beziehung" darum, dass mehrere Slice-Reduzierer dieselbe Aktion abhören, wobei jeder seinen Statusbit unabhängig aktualisiert. Ja, in den meisten Fällen gibt es möglicherweise nur einen Reduzierer, der sich um eine bestimmte Aktion kümmert, aber die Reaktion mehrerer Reduzierer ist für Redux ein beabsichtigter Anwendungsfall.

@markerikson

Zweitens können Sie definitiv denselben Reduzierer haben, der auf mehrere Aktionstypen wartet und diese auf dieselbe Weise behandelt.

Ich habe tatsächlich Redux-Aktionen verwendet und wusste nicht, dass es eine Dienstprogrammmethode combinAction gibt, die die Aufgabe erledigt. Ich habe es gerade gefunden und Ihre obige Aussage erwähnt dasselbe. (hätte klarer sein sollen, sorry)
Vielen Dank für Ihre Antwort.

Ich denke nicht, dass Ducks in ActionTypes richtig definiert:

IST haben Aktionstypen in Form NPM-Modul-or-app / Abschwächer / ACTION_TYPE

Der Grund ist, dass eine Aktion von mehr als einem Reduzierer abonniert werden kann. Wie @mherodev sagte, ist const ACTION = 'app/feature/ACTION'; sinnvoller, außerdem möchte ich ein Aktionsschema const ACTION = 'action://app/feature/ACTION'; .

Warum nicht einfach eine global inkrementelle Ganzzahl verwenden, um die Aktionstypen zu identifizieren? z.B

let id = 0

function generateActionType (label /* for readability */) {
  id++
  return `app/feature/${id}/${label}`
}

Um ein wenig darüber nachzudenken, schreiben wir eine Flusenregel, die in ein Verzeichnis schaut (in dem wir alle Reduzierer haben und jeder Reduzierer seine eigenen Aktionstypen als Konstanten hat), um sicherzustellen, dass wir nicht mit anderen Entwicklern in Konflikt geraten.

Wir planen, so zu fusseln, dass ein Verstoß gemeldet wird, wenn zwei Konstanten mit demselben Wert vorhanden sind.

Wenn es einen Vorschlag gibt, lass es mich wissen :)

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen