Redux: exemples - comment implémenter un espace de noms de type action?

Créé le 23 sept. 2015  ·  32Commentaires  ·  Source: reduxjs/redux

J'ai passé environ une semaine maintenant à essayer de trouver de bonnes implémentations d'espacement de noms de type d'action et j'ai lancé la mienne qui est basée sur des classes de transition qui contiennent la définition d'un créateur d'action, d'un réducteur et d'une liste de ses transitions enfants, mais cette approche nécessite un moyen trop de code de plaque de chaudière et c'est problématique lorsque vous avez beaucoup d'actions très simples.

Dans tous les exemples que vous pouvez trouver dans ce référentiel et dans beaucoup d'autres, ce problème est toujours ignoré pour une raison quelconque, mais c'était le premier problème que j'ai repéré lorsque j'ai commencé à examiner Redux.

Est-ce que j'ai râté quelque chose ? Vous n'avez pas de collisions de dénomination de type d'action?

Quelqu'un peut-il mettre à jour les exemples et me montrer comment vous gérez ce problème ou me diriger dans la bonne direction?

Mon cas spécifique est lié au fait d'avoir 2 panneaux d'administration séparés sur le frontend. Un pour les testeurs et un pour les clients dont l'état sera stocké dans des sections séparées du magasin ("testerAccount", "customerAccount"), mais les deux pourront faire des choses similaires pour par exemple ADD_VIDEO_UPLOAD, ADD_COMMENT, etc, etc.

J'apprécierais vraiment votre aide ici :)

question

Commentaire le plus utile

Les chaînes ne sont pas intrinsèquement mauvaises pour l'espacement des noms. Les URL sont des chaînes et elles semblent fonctionner correctement.

Tous les 32 commentaires

Mettre accountType en actions? Voir aussi examples/real-world/reducers pour savoir comment écrire une fabrique de réducteurs et utiliser le même code plusieurs fois pour produire des réducteurs qui répondent à différentes actions.

De même, n'oubliez pas que les créateurs d'actions ne sont que des fonctions, et vous pouvez créer des fonctions qui renvoient un tas d'autres fonctions.

Je pense que ce serait vraiment utile si vous donniez un petit exemple précis du problème que vous dites ignoré dans les exemples existants.

Nous pouvons ensuite vous aider à trouver des moyens de simplifier cet exemple spécifique. Il est très difficile de suggérer quelque chose de spécifique en réponse à une question sans code.

@gaearon Je sais où vous vous

J'essaie d'isoler des groupes d'actions les uns des autres afin que 2 personnes travaillent sur deux sections différentes du système et m'assurent qu'elles ne commenceront pas à se déclencher accidentellement mutuellement en raison du manque d'espacement des noms dans les noms des types d'action.

J'espère que cela rend les choses plus claires.

J'ai résolu ce problème dans mon (mes) projet (s) en utilisant https://www.npmjs.com/package/flux-constant en combinaison avec une fonction d'aide super simple appelée createActionTypes .

Essentiellement, mon code finit par être:

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

Pourquoi ne pas conserver tous les types d'action sous forme de constantes au même endroit?
Ensuite, ils ne peuvent certainement pas entrer en conflit car vous ne pouvez pas exporter le même nom deux fois.

Pour mieux visualiser cela, disons que c'est l'état initial du magasin:


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

Comment allez-vous éviter les collisions de noms de types d'action lors de la mise en œuvre de réducteurs séparés pour l'ajout de vidéos ou de messages pour chacune de ces sections?

@gaearon ce que vous suggérez ne résout pas le cœur du problème, c'est plutôt une approche de ruban adhésif car avec le temps, vous vous retrouverez avec un fichier de type énorme qui causera beaucoup de problèmes.

Cela posera immédiatement des problèmes lors des fusions de code qui devront plus tard être résolus avec des hacks de nommage, par exemple:

ADD_VIDEO_UPLOAD, ADD_TESTER_VIDEO_UPLOAD, ADD_VIDEO_UPLOAD_IN_SOME_SECTION, etc.

qui est encore un gros mal de tête.

@koulmomo C'est exactement ce que je cherchais, merci :): +1: Simple et puissant.

@gaearon Je pense que nous devrions avoir au moins un exemple qui utilise soit ce package, soit un nouveau package qui pourrait être nommé redux-constant?

À mon avis, la promotion de l'approche dans les documents / exemples, qui résout le problème de l'espace de noms devrait être la nouvelle valeur par défaut.

Merci pour votre aide :)

Dans votre exemple, je ne comprends pas vraiment pourquoi ne pas avoir un ensemble de types, un générateur de réducteur et un ensemble de créateurs d'action. Les actions contiendraient la propriété accountType pour les distinguer. Les créateurs d'action l'accepteraient comme paramètre. La fabrique de réducteurs accepterait accountType et renverrait un réducteur qui ne gère que les actions avec ce type de compte.

Je ne pense pas que flux-constant soit une excellente solution ici. Il semble reposer sur des chèques instanceof . Ce qui signifie que vous ne pouvez pas sérialiser vos actions et les rejouer plus tard car - bam! - leurs types désérialisés ne correspondent pas à ceux générés.

Les gens essaient souvent de rendre Flux «plus simple» sans se rendre compte qu'ils brisent ses caractéristiques essentielles .

Dans votre exemple, je ne comprends pas vraiment pourquoi ne pas avoir un ensemble de types, un générateur de réducteur et un ensemble de créateurs d'action. Les actions contiendraient la propriété accountType pour les distinguer. Les créateurs d'action l'accepteraient comme paramètre. La fabrique de réducteurs accepterait accountType et renverrait un réducteur qui ne gère que les actions avec ce type de compte.

J'ai été induit en erreur par la symétrie apparente de votre exemple. Je comprends maintenant que vous vouliez dire qu'il n'y a pas de DRY ici, et la symétrie des fonctionnalités n'est apparente que comme vous l'avez dit dans https://github.com/rackt/redux/issues/786#issuecomment -142649749.

Je ne pense pas qu'il soit nécessaire d'avoir une bibliothèque ici. Établissez une convention! Par exemple, si vos sous-projets ou fonctionnalités sont si séparés, définissez une règle pour appeler les types d'actions feature/ACTION_TYPE , par exemple testers/UPLOAD_VIDEO ou customers/UPLOAD_VIDEO . Ceci est particulièrement intéressant si ces «groupes» correspondent en fait à de vrais dossiers (ou mieux à des packages) sur le disque.

Les violations seront faciles à détecter dans les révisions de code. Si vous le souhaitez, vous pouvez automatiser cela, mais je ne vois pas ce que cela apporte avec l'espace de noms manuel. À l'intérieur de chaque module, vous voudrez toujours déclarer toutes les constantes dans un seul fichier pour un contrôle plus facile des fonctionnalités, pour éviter la duplication accidentelle des efforts et en tant que documentation.

Je suis tout à fait favorable à l'ajout d'un exemple huge-apps avec le fractionnement du code, les types d'actions d'espacement de noms, etc. Ce serait un problème distinct.

Les chaînes ne sont pas intrinsèquement mauvaises pour l'espacement des noms. Les URL sont des chaînes et elles semblent fonctionner correctement.

@gaearon Vous avez raison, j'ai oublié le problème de la sérialisation avec la solution basée sur la constante de flux.

Je n'ai pas de problème avec l'espacement de noms basé sur des chaînes tant que nous pouvons facilement distinguer les modules / espaces de noms dans le nom lui-même.

La solution basée sur la convention de dénomination est quelque chose que j'ai déjà vu:

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

mais j'espérais qu'il pourrait y avoir une meilleure ou une manière «correcte» de faire ceci qui serait basée sur votre expérience.

Je pense que je vais essayer de convertir l'approche à constante de flux en quelque chose que vous pourriez sérialiser d'une manière ou d'une autre.

S'il vous plaît, ajoutez un exemple d '"applications énormes" si vous trouvez du temps pour cela car cela fera gagner beaucoup de temps à d'autres personnes et peut-être que cela conduira à établir une sorte de convention que nous pourrions facilement suivre plus tard.

Merci encore :)

Oui, désolé, malheureusement, je n'ai pas travaillé sur une grosse application depuis un certain temps, et même quand je l'ai fait, j'ai en fait aimé que nous ayons un seul fichier géant avec des constantes regroupées en sections car il donne un bon aperçu de ce qui peut arriver dans l'appli.

Je chercherais à voir ce "problème" expliqué plus en détail dans la documentation. J'ai / ai eu les mêmes questions que @pbc. Je ne pense pas "Pourquoi ne pas conserver tous les types d'action sous forme de constantes au même endroit?" fonctionne si vous réutilisez plusieurs modules dans les projets et la recommandation "Établissez une convention!" ressemble beaucoup à quelque chose comme BEM dans CSS land. Cependant, nous nous éloignons des modules BEM pour les modules CSS et je suppose que quelque chose de similaire aux modules CSS dont les noms de classe de hachage sont également nécessaires pour les types d'action dans les grands projets.

Pour autant que je sache, il n'y a aucun moyen de réaliser à la fois la sérialisation et l'absence de conflits.

Une bonne convention pour les modules réutilisables pourrait utiliser des «fournisseurs d'unicité» déjà existants, comme les noms de domaine inversés, ou le nom d'utilisateur / dépôt sur github, ou le nom de module enregistré sur npm.


EDIT: Les modules CSS le font en définissant un langage personnalisé par-dessus le CSS et en préfixant les noms de classe pendant le prétraitement (se résume de toute façon à une convention, mais une version générée).

+1 pour la question et la discussion. J'ai également beaucoup lutté avec ce problème exact en tant que @pbc . La confusion pour moi est que nous avons combinerReducers pour les réducteurs, ce qui fait un bel arbre d'état, mais les actions en revanche ressemblent plus ou moins à un global. À première vue, tout type d'espacement de noms doit être effectué manuellement.

Je pense avoir trouvé une solution pour les types d'actions de chaîne sérialisables. Je pense que c'est une de ces situations où notre esprit impose des limites artificielles à quelque chose.

L'idée de base est que les constantes de type n'ont pas besoin d'être liées de quelque manière que ce soit à la valeur de la chaîne. Vous pouvez donc utiliser des valeurs générées aléatoirement, des chemins de fichiers hachés ou tout autre élément unique pour la valeur de chaîne constante de type. Dans votre réducteur, vous importez la constante de type par nom et la comparez par nom. Ce nom peut être utilisé par d'autres actions et dans d'autres réducteurs, mais cela n'a pas d'importance, car la valeur ne sera pas la même.

Exemple ici: https://gist.github.com/samsch/63a54e868d7fa2b6023a

C'est raisonnable. Vous souhaitez toujours conserver une partie lisible par l'homme dans le nom de l'action, mais la génération de préfixes uniques fonctionne totalement.

Si vous pouvez prouver qu'ils sont uniques, bien sûr.
Le meilleur moyen est toujours d'utiliser des fournisseurs d'unicité déjà existants: noms de domaine inversés ou noms de module npm.

L'espacement de noms avec convention a ses risques lorsque vous écrivez des modules qui peuvent être utilisés à divers endroits hors de votre contrôle.

Disons que vous avez un module "Geometry", avec ActionType de "Area". Ce module est utilisé à deux endroits:

  1. AppA-> Dessin-> Géométrie (namespace = "Drawing / Geometry / Area")
  2. AppB-> Trigonométrie-> Forme-> Géométrie (namespace = "Trigonométrie / Forme / Zone")

Vous avez maintenant deux espaces de noms en conflit, selon l'endroit où votre module est utilisé.

  • Ce n'est pas une bonne idée de coder en dur ce chemin complet dans votre module Geometry pour ActionType "Area".
  • Au contraire, gardez le nom simple: "Zone".
  • De la même manière que pour la composition du réducteur, composez l'espace de noms en demandant à chaque parent contenant d'ajouter un préfixe.

J'expérimente le modèle suivant:

créez un répertoire pour chaque type de collection qui contient:

  • consts
  • réducteur
  • Composants
  • sagas

créer des consts au format suivant:

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

Lors de l'utilisation de constantes dans des réducteurs, des saga ou des actions import les toutes avec * :

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

Malheureusement pas génial pour secouer les arbres. Ce serait bien si ES6 permettait:

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

Quelqu'un sait si Webpack 2 peut intelligemment secouer l'arbre import * afin qu'il ne regroupe pas le code pour les exportations qui ne sont pas utilisées même si elles sont importées avec * ?

Cela semble être un problème très courant et il apparaît souvent dans notre bureau, soit:

  • Les conflits de noms provoquent un comportement indésirable.
  • Se plaindre du passe-partout associé à la création de types d'actions uniques.

Nous avons essayé plusieurs solutions différentes mais rien ne semblait coller:

  1. Conserver tous les types d'action en tant que constantes au même endroit facilite certainement la gestion, mais j'ai trouvé que cela devenait un peu compliqué lorsque l'application se développait.
  2. Les valeurs générées aléatoirement mentionnées par @samsch ont vraiment attiré mon attention et cela fonctionne certainement, mais oui, perdre la partie lisible par l'homme rend plus difficile à vivre.
  3. Le commentaire ci-dessus de @philholden m'a vraiment séduit, car nous utilisons généralement une architecture axée sur les fonctionnalités et la structure des répertoires est assez descriptive.

Après avoir expérimenté ces derniers mois, nous avons décidé d'utiliser la structure de répertoires pour nommer nos types d'actions. Les taper manuellement vieillissent très vite, alors j'ai essayé de faire quelque chose avec __filename mais cela ne fonctionne pas à cause du regroupement de code, etc. Ensuite, j'ai créé mon premier plugin Babel qui transforme le mot-clé __filenamespace dans une chaîne statique, qui semble bien fonctionner.

Exemple

En 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';

N'hésitez pas à l'essayer, j'espère que c'est utile. Ce serait fantastique d'avoir des commentaires, en particulier de @pbc ou @gaearon en tant que créateur du problème et créateur de la bibliothèque.

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

J'espérais vraiment que quelqu'un proposerait un "standard". Nous n'avons pas besoin de symboles ou d'objets pour cela, nous avons juste besoin d'une convention.

Allons-nous faire "featureName$actionType" ou "fileName/ACTION_TYPE" ou "PROJECT.FEATURE.ACTION" ? Si nous pouvons tous nous entendre sur quelque chose, il sera plus facile de partager les réducteurs.

Je pense qu'avec la pénurie d'autres conventions disponibles, Ducks est devenu la norme de facto.

const ACTION = 'app/feature/ACTION'; est assez suffisant.

@gaearon a toujours mentionné que la relation 1: 1 entre les actions et les réducteurs est probablement une mauvaise idée et je suis vraiment d'accord. Mais qu'en est-il du cas où deux vues différentes veulent en fait appeler le même réducteur.
Pour par exemple. Une simple bascule de préférence peut être activée ou désactivée à partir de deux endroits différents:
/ myaccount / toggleNotification
/ dashboard / toggleNotification
donc devrions-nous avoir deux réducteurs écrits avec le même contenu dans
réducteurs / notifications.js
cc: @ samit4me , @philholden


Juste une autre pensée, je pense que c'est une bonne idée, d'avoir deux ou plusieurs réducteurs dans un fichier faisant le même travail avec des noms d'action différents. C'est ainsi qu'en voyant simplement le réducteur, nous pouvons comprendre qu'à partir de combien d'endroits différents un état particulier est modifié.

Quoi qu'il en soit, je serais ravi d'entendre les experts. Merci.

@ivks : plusieurs réflexions ici.

Tout d'abord, vous pouvez distribuer la même action à partir de plusieurs emplacements dans l'application.

Deuxièmement, vous pouvez certainement avoir le même réducteur à l'écoute de plusieurs types d'action et à les gérer de la même manière.

Troisièmement, l'aspect "relation 1: 1" consiste à pouvoir avoir plusieurs réducteurs de tranche écoutant la même action, chacun mettant à jour son bit d'état indépendamment. Oui, la plupart du temps, il se peut qu'il n'y ait qu'un seul réducteur qui se soucie d'une action donnée, mais la réponse de plusieurs réducteurs est vraiment un cas d'utilisation prévu pour Redux.

@markerikson

Deuxièmement, vous pouvez certainement avoir le même réducteur à l'écoute de plusieurs types d'action et à les gérer de la même manière.

J'utilisais en fait des actions redux, et je ne savais pas qu'il avait une méthode utilitaire combineAction qui fera la tâche. Je viens de le trouver et votre déclaration ci-dessus mentionne simplement la même chose. (aurait dû être plus clair, désolé)
Merci beaucoup d'avoir répondu.

Je ne pense pas que Ducks définit correctement dans ActionTypes:

Doivent avoir des types d'action sous la forme NPM-module-ou-app / réducteur / ACTION_TYPE

La raison en est qu'une action peut être souscrite par plus d'un réducteur. Comme @mherodev l'a dit, const ACTION = 'app/feature/ACTION'; plus de sens, j'aimerais en plus ajouter un schéma d'action: const ACTION = 'action://app/feature/ACTION'; .

Pourquoi ne pas simplement utiliser un entier auto-incrémentiel global pour identifier les types d'actions? par exemple

let id = 0

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

Réfléchi un peu à cela, pour nous assurer de ne pas entrer en conflit avec d'autres développeurs, nous écrivons une règle de lint, qui regarde dans un répertoire (où nous avons tous les réducteurs et chaque réducteur a ses propres types d'action comme constantes)

Nous prévoyons de pelucher de sorte qu'il signale une violation s'il y a deux constantes avec la même valeur.

S'il y a une suggestion, faites-le moi savoir :)

Cette page vous a été utile?
0 / 5 - 0 notes