Redux: ejemplos: ¿cómo implementar el espacio de nombres de tipo de acción?

Creado en 23 sept. 2015  ·  32Comentarios  ·  Fuente: reduxjs/redux

He pasado aproximadamente una semana tratando de encontrar buenas implementaciones del espacio de nombres de tipos de acción y he lanzado el mío, que se basa en clases de transición que contienen la definición de un creador de acción, un reductor y una lista de sus transiciones secundarias, pero este enfoque requiere mucho demasiado código de placa de caldera y es problemático cuando tiene muchas acciones muy simples.

En todos los ejemplos que puede encontrar en este repositorio y en muchos otros, este problema siempre se ignora por alguna razón, pero este fue el primer problema que detecté cuando comencé a buscar en Redux.

Me estoy perdiendo de algo ? ¿Ustedes no tienen colisiones de nombres de tipo de acción?

¿Alguien puede actualizar los ejemplos y mostrarme cómo maneja este problema o indicarme la dirección correcta?

Mi caso específico está relacionado con tener 2 paneles de administración separados en la interfaz. Uno para probadores y otro para clientes que tendrán su estado almacenado en secciones separadas de la tienda ("testerAccount", "customerAccount"), pero ambos podrán hacer cosas similares, por ejemplo, ADD_VIDEO_UPLOAD, ADD_COMMENT, etc., etc.

Realmente agradecería su ayuda aquí :)

question

Comentario más útil

Las cadenas no son inherentemente malas para el espacio de nombres. Las URL son cadenas y parecen funcionar bien.

Todos 32 comentarios

¿Poner accountType en acciones? Vea también examples/real-world/reducers para saber cómo puede escribir una fábrica de reductores y usar el mismo código muchas veces para producir reductores que respondan a diferentes acciones.

Del mismo modo, no olvide que los creadores de acciones son solo funciones, y puede crear funciones que devuelvan un montón de otras funciones.

Creo que realmente ayudaría si proporcionara un pequeño ejemplo específico del problema que dice que se ignora en los ejemplos existentes.

Luego, podemos ayudarlo a encontrar formas de simplificar ese ejemplo específico. Es muy difícil sugerir algo específico en respuesta a una pregunta sin código.

@gaearon Sé hacia dónde te

Estoy tratando de aislar grupos de acciones entre sí para poder tener a 2 personas trabajando en dos secciones diferentes del sistema y estar seguro de que no comenzarán a activar los reductores entre sí accidentalmente debido a la falta de espacio de nombres en los nombres de los tipos de acción.

Espero que esto lo deje más claro.

Resolví este problema en mi (s) proyecto (s) usando https://www.npmjs.com/package/flux-constant en combinación con una función auxiliar súper simple llamada createActionTypes .

Esencialmente, mi código termina siendo:

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

¿Por qué no mantener todos los tipos de acciones como constantes en un solo lugar?
Entonces seguro que no pueden chocar porque no se puede exportar el mismo nombre dos veces.

Para visualizar mejor esto, digamos que este es el estado inicial de la tienda:


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

¿Cómo evitará las colisiones de nombres de tipos de acción al implementar reductores separados para agregar videos o mensajes para cada una de estas secciones?

@gaearon lo que está sugiriendo no resuelve el núcleo del problema, es más un enfoque de cinta adhesiva porque con el tiempo terminará con un archivo de tipo enorme que causará muchos problemas.

Inmediatamente causará problemas durante las fusiones de código que más adelante deberán resolverse con trucos de nombres, por ejemplo:

ADD_VIDEO_UPLOAD, ADD_TESTER_VIDEO_UPLOAD, ADD_VIDEO_UPLOAD_IN_SOME_SECTION, etc.

que es un gran dolor de cabeza de nuevo.

@koulmomo Esto es exactamente lo que estaba buscando, gracias :): +1: Simple y poderoso.

@gaearon Creo que deberíamos tener al menos un ejemplo que use este paquete o un nuevo paquete que podría llamarse redux-constante.

En mi opinión, el enfoque de promoción en los documentos / ejemplos, que resuelve el problema del espacio de nombres, debería ser el nuevo predeterminado.

Gracias por tu ayuda con esto :)

En su ejemplo, realmente no entiendo por qué no tener un conjunto de tipos, un generador de reductores y un conjunto de creadores de acciones. Las acciones contendrían la propiedad accountType para diferenciarlas. Los creadores de acciones lo aceptarían como parámetro. La fábrica de reductores aceptaría accountType y devolvería un reductor que solo maneja acciones con este tipo de cuenta.

No creo que flux-constant sea ​​una gran solución aquí. Parece depender de instanceof cheques. Lo que significa que no puede serializar sus acciones y luego reproducirlas porque, ¡bam !, sus tipos deserializados no coincidirán con los generados.

Las personas a menudo intentan hacer que Flux sea "más simple" sin darse cuenta de que están rompiendo sus características esenciales .

En su ejemplo, realmente no entiendo por qué no tener un conjunto de tipos, un generador de reductores y un conjunto de creadores de acciones. Las acciones contendrían la propiedad accountType para diferenciarlas. Los creadores de acciones lo aceptarían como parámetro. La fábrica de reductores aceptaría accountType y devolvería un reductor que solo maneja acciones con este tipo de cuenta.

Me engañó la aparente simetría de su ejemplo. Ahora entiendo que quisiste decir que no hay DRY aquí, y la simetría en las características solo es aparente como dijiste en https://github.com/rackt/redux/issues/786#issuecomment -142649749.

No creo que haya necesidad de una biblioteca aquí. ¡Establece una convención! Por ejemplo, si sus subproyectos o características están tan separados, establezca una regla para llamar a los tipos de acción feature/ACTION_TYPE , por ejemplo, testers/UPLOAD_VIDEO o customers/UPLOAD_VIDEO . Esto es especialmente bueno si estos "grupos" realmente corresponden a carpetas reales (o mejor, paquetes) en el disco.

Las infracciones serán fáciles de detectar en las revisiones de código. Si _realmente_ quieres, puedes automatizar esto, pero no puedo ver qué aporta el espacio de nombres manual. Dentro de cada módulo, aún querrá declarar todas las constantes en un archivo para un control más fácil de las funciones, evitando la duplicación accidental de esfuerzos y como documentación.

Estoy totalmente a favor de agregar un ejemplo de huge-apps con división de código, tipos de acciones con espacios de nombres, etc. Sin embargo, eso sería un problema aparte.

Las cadenas no son inherentemente malas para el espacio de nombres. Las URL son cadenas y parecen funcionar bien.

@gaearon Tienes razón, me olvidé del problema de la serialización con la solución basada en la constante de flujo.

No tengo ningún problema con el espacio de nombres basado en cadenas siempre que podamos distinguir fácilmente los módulos / espacios de nombres en el nombre mismo.

La solución basada en la convención de nomenclatura es algo que he visto antes:

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

pero esperaba que hubiera una forma mejor o "correcta" de hacer esto que se basara en su experiencia.

Creo que intentaré convertir el enfoque de constante de flujo en algo que realmente pueda serializar de alguna manera.

Agregue un ejemplo de "aplicaciones gigantes" si encuentra tiempo para esto, ya que esto le ahorrará mucho tiempo a otras personas y tal vez lleve a establecer algún tipo de convención que podamos seguir fácilmente más adelante.

Gracias de nuevo :)

Sí, lo siento, desafortunadamente no he estado trabajando en una gran aplicación durante algún tiempo, e incluso cuando lo hice, en realidad me gustó que tenemos un solo archivo gigante con constantes agrupadas en secciones porque brinda una buena descripción general de lo que puede suceder. en la aplicación.

Buscaría ver este "problema" explicado con más detalle en los documentos. Tengo / tuve las mismas preguntas como @pbc. No pienso "¿Por qué no mantener todos los tipos de acción como constantes en un solo lugar?" funciona si reutiliza varios módulos en todos los proyectos y la recomendación "¡Establezca una convención!" suena muy parecido a algo como BEM en CSS land. Sin embargo, nos alejamos de los módulos BEM a CSS y supongo que algo similar a los módulos CSS, cuyos nombres de clase hash también se necesitan para los tipos de acción en grandes proyectos.

Por lo que puedo decir, no hay forma de lograr tanto la serialización como la ausencia de conflictos.

Una buena convención para módulos reutilizables podría usar "proveedores de exclusividad" ya existentes, como nombres de dominios inversos, o nombre de usuario / repositorio en github, o nombre de módulo registrado en npm.


EDITAR: Los módulos CSS hacen esto definiendo un lenguaje personalizado sobre CSS y prefijando los nombres de clase durante el preprocesamiento (se reduce a la convención de todos modos, pero a uno generado).

+1 para la pregunta y la discusión. También luché mucho con este problema exacto como @pbc . La confusión para mí es que tenemos combineReducers para reductores, lo que hace un buen árbol de estado, pero las acciones, por otro lado, se sienten más o menos como globales. Por lo que parece, cualquier tipo de espacio de nombres debe hacerse manualmente.

Creo que encontré una solución para los tipos de acciones de cadena serializables. Creo que es una de esas situaciones en las que nuestras mentes imponen limitaciones artificiales a algo.

La idea básica es que las constantes de tipo no necesitan estar relacionadas de ninguna manera con el valor de la cadena. Por lo tanto, puede usar valores generados aleatoriamente, rutas de archivos con hash o cualquier otra cosa única para el tipo de valor de cadena constante. En su reductor, importa la constante de tipo por nombre y la compara por nombre. Ese nombre puede ser usado por otras acciones y en otros reductores, pero no importa, porque el valor no será el mismo.

Ejemplo aquí: https://gist.github.com/samsch/63a54e868d7fa2b6023a

Esto es sensato. Aún desea conservar alguna parte legible por humanos en el nombre de la acción, pero generar prefijos únicos funciona totalmente.

Si puedes demostrar que son únicos, claro.
La mejor manera sigue siendo utilizar proveedores de exclusividad ya existentes: nombres de dominio inversos o nombres de módulo npm.

El espacio de nombres con convención tiene sus peligros cuando escribe módulos que se pueden usar en varios lugares que están fuera de su control.

Digamos que tienes un módulo "Geometría", con ActionType de "Área". Este módulo se utiliza en dos lugares:

  1. AppA-> Dibujo-> Geometría (espacio de nombres = "Dibujo / Geometría / Área")
  2. AppB-> Trigonometry-> Shape-> Geometry (espacio de nombres = "Trigonometry / Shape / Area")

Ahora tiene dos espacios de nombres en conflicto, dependiendo de dónde se use su módulo.

  • No es una buena idea codificar esta ruta completa en su módulo de geometría para ActionType "Area".
  • Más bien, mantenga el nombre simple: "Área".
  • De manera similar a la composición del reductor, componga el espacio de nombres haciendo que cada padre que lo contenga agregue un prefijo.

Estoy experimentando con el siguiente patrón:

crea un directorio para cada tipo de colección que contiene:

  • consts
  • reductor
  • componentes
  • sagas

crear consts en el siguiente formato:

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

Al usar constantes en reductores, saga o acciones import todos ellos con * :

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

Lamentablemente, no es ideal para sacudir árboles. Sería bueno si ES6 permitiera:

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

¿Alguien sabe si Webpack 2 puede agitar inteligentemente los árboles import * para que no agrupe el código para las exportaciones que no se utilizan incluso si se importan con * ?

Este parece ser un problema muy común y aparece a menudo en nuestra oficina, ya sea:

  • Los conflictos de nombres provocan un comportamiento no deseado.
  • Quejarse de la repetición asociada con la creación de tipos de acción únicos.

Probamos algunas soluciones diferentes, pero nada parecía funcionar:

  1. Mantener todos los tipos de acción como constantes en un solo lugar ciertamente hace que sea más fácil de administrar, pero descubrí que se vuelve un poco difícil de manejar cuando la aplicación crece.
  2. Los valores generados aleatoriamente mencionados por @samsch realmente me llamaron la atención y ciertamente funciona, pero sí, perder la parte legible por humanos hace que sea más difícil vivir con ellos.
  3. El comentario anterior de @philholden realmente me atrajo, ya que normalmente usamos una arquitectura basada en características y la estructura de directorios es bastante descriptiva.

Después de experimentar durante los últimos meses, decidimos usar la estructura de directorio para el espacio de nombres de nuestros tipos de acción. Escribirlos manualmente envejece muy rápido, así que intenté hacer algo con __filename pero esto no funciona debido al paquete de código, etc. Luego hice mi primer complemento de Babel que transforma la palabra clave __filenamespace en una cadena estática, que parece funcionar bien.

Ejemplo

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

Siéntete libre de probarlo, espero que sea útil. Sería fantástico recibir comentarios, especialmente de @pbc o @gaearon como creador de problemas y creador de bibliotecas.

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

Realmente esperaba que alguien ofreciera un "estándar". No necesitamos símbolos u objetos para esto, solo necesitamos una convención.

¿Vamos a hacer "featureName$actionType" o "fileName/ACTION_TYPE" o "PROJECT.FEATURE.ACTION" ? Si todos podemos estar de acuerdo en algo, será más fácil compartir reductores.

Creo que con la escasez de otras convenciones disponibles, Ducks se ha convertido en el estándar de facto.

const ACTION = 'app/feature/ACTION'; es suficiente.

@gaearon siempre ha mencionado que la relación 1: 1 entre acciones y reductores es probablemente una mala idea y estoy realmente de acuerdo. Pero ¿qué pasa con el caso en el que dos puntos de vista diferentes realmente quieren llamar al mismo reductor?
Por ej. Se puede habilitar o deshabilitar una simple alternancia de preferencias desde dos lugares diferentes:
/ myaccount / toggleNotification
/ dashboard / toggleNotification
así que deberíamos tener dos reductores escritos con el mismo contenido en
reductores / notificaciones.js
cc: @ samit4me , @philholden


Solo en otro pensamiento, creo que esta es una buena idea, tener dos o más reductores dentro de un archivo haciendo el mismo trabajo con diferentes nombres de acción. Así es como con solo ver el reductor podemos entender que desde cuántos lugares diferentes se modifica un estado en particular.

De todos modos, me encantaría escuchar a los expertos. Gracias.

@ivks : varios pensamientos aquí.

Primero, puede enviar la misma acción desde varios lugares dentro de la aplicación.

En segundo lugar, definitivamente puede tener el mismo reductor escuchando múltiples tipos de acción y manejándolos de la misma manera.

En tercer lugar, el aspecto de la "relación 1: 1" se trata de poder tener varios reductores de corte escuchando la misma acción, y cada uno actualiza su bit de estado de forma independiente. Sí, la mayor parte del tiempo puede que solo haya un reductor que se preocupe por una acción determinada, pero que varios reductores respondan es un caso de uso previsto para Redux.

@markerikson

En segundo lugar, definitivamente puede tener el mismo reductor escuchando múltiples tipos de acción y manejándolos de la misma manera.

De hecho, estaba usando redux-actions, y no sabía que tenía un método de utilidad combineAction que hará la tarea. Lo acabo de encontrar y su declaración anterior solo menciona lo mismo. (debería haber sido más claro, lo siento)
Muchas gracias por contestar.

No creo que Ducks defina correctamente en ActionTypes:

DEBE tener tipos de acción con el formato npm-module-or-app / reducer / ACTION_TYPE

El motivo es que una acción puede estar suscrita por más de un reductor. Como dijo @mherodev , const ACTION = 'app/feature/ACTION'; tiene más sentido, además me gustaría agregar un esquema para la acción: const ACTION = 'action://app/feature/ACTION'; .

¿Por qué no usar simplemente un entero auto incremental globalmente para identificar los tipos de acción? p.ej

let id = 0

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

Pensando un poco en esto, para asegurarnos de que no chocamos con otros desarrolladores, estamos escribiendo una regla de pelusa, que busca en un directorio (donde tenemos todos los reductores y cada reductor tiene sus propios tipos de acción como constantes)

Estamos planeando aplicar un linting de manera que informe una violación si hay dos constantes con el mismo valor.

Si hay alguna sugerencia, hágamelo saber :)

¿Fue útil esta página
0 / 5 - 0 calificaciones