javascript - helmet - react title
¿Cómo poner métodos en los objetos en el estado de Redux? (3)
En Redux, realmente no tienes modelos personalizados. Su estado debe ser objetos simples (o registros inmutables). No se espera que tengan ningún método personalizado.
En lugar de poner métodos en los modelos (por ejemplo, TodoItem.rename
), se espera que escriba reductores que manejen acciones . Ese es el objetivo de Redux.
// Manages single todo item
function todoItem(state, action) {
switch (action.type) {
case ''ADD'':
return { name: action.name, complete: false };
case ''RENAME'':
return { ...state, name: action.name };
case ''TOGGLE_COMPLETE'':
return { ...state, complete: !state.complete };
default:
return state;
}
}
// Manages a list of todo items
function todoItems(state = [], action) {
switch (action.type) {
case ''ADD'':
return [...state, todoItem(undefined, action)];
case ''REMOVE'':
return [
...state.slice(0, action.index),
...state.slice(action.index + 1)
];
case ''RENAME'':
case ''TOGGLE_COMPLETE'':
return [
...state.slice(0, action.index),
todoItem(state[action.index], action),
...state.slice(action.index + 1)
];
}
}
Si esto todavía no tiene sentido, lea el tutorial básico de Redux porque parece que tiene una idea equivocada sobre cómo se estructuran las aplicaciones de Redux.
De acuerdo con el estado de la aplicación docs reaccionar tiene que ser algo serializable. ¿Qué hay de las clases entonces?
Digamos que tengo una aplicación ToDo. Cada uno de los artículos de Todo
tiene propiedades como el name
, la date
, etc. hasta ahora, todo bien. Ahora quiero tener métodos en objetos que no son serializables. Es decir, Todo.rename()
que cambiaría el nombre de todo y haría muchas otras cosas.
Por lo que yo entiendo, puedo tener la función declarada en algún lugar y rename(Todo)
o tal vez pasar esa función a través de los accesorios this.props.rename(Todo)
al componente.
Tengo 2 problemas para declarar .rename()
algún lugar: 1) ¿Dónde? En reductor? Sería difícil encontrar todos los métodos de would be instance
en algún lugar de los reductores de la aplicación. 2) Pasando esta función alrededor. De Verdad? ¿Debería pasarlo manualmente de todos los componentes de nivel superior a través de Y cada vez que tengo más métodos, agrego una tonelada de texto repetitivo para simplemente pasarlo? O siempre lo hago y espero que solo tenga un método de cambio de nombre para un tipo de objeto. No Todo.rename()
Task.rename()
y Event.rename()
Eso me parece tonto. Object debería saber qué se puede hacer y de qué manera. ¿No?
¿Qué me falta aquí?
La respuesta de Dan es, por supuesto, correcta en cuanto a cómo usar redux al escribir una aplicación desde cero. Mi respuesta se basa en una situación de motivación no ideal que puede ser similar a la situación de OP y cómo estoy pensando en enfrentarlo.
Me encuentro intentando crear una aplicación redux que dependa de bibliotecas de terceros dentro del reductor redux que realizan operaciones en objetos de entrada con métodos, y quería mantener estos objetos de entrada en el estado de reducción. Aquí está mi situación de motivación en pseudocódigo, y cómo lo estoy "resolviendo" usando el método de asignación de lodash:
// Library A is a large 3rd party library I don''t want to change
// Library A expects instances of MyClass to have a method ''complexOperation'', and also to have property ''a''
LibraryA = class {
static reliesOnComplexOperation(myClassInstance) {
if (myClassInstance.complexOperation() && myClassInstance.a < 100) {
return true;
} else {
return false;
}
}
}
// MyClass is my own class that I control, and I want to store it''s state and make changes to it using the redux reducer.
MyClass = class {
constructor() {
this.a = 0;
}
myComplexOperation() {
return a > 10;
}
}
//and here is my redux reducer, making use of Library A
function reducer(state, action) {
switch (action.type) {
case CHANGE_MY_CLASS:
const myClassInstancePlain = state.myClassInstance;
// redux has converted myClassInstance into a plain object with no methods, so we need to create a new instance of MyClass, and assign property values from the plain object to the new instance. Using the assign function from lodash library to achieve this - likely not the most efficient way
const myClassInstance = _.assign(new MyClass(), myClassInstancePlain);
// now I can pass myClassInstance off to LibraryA, and the complexOperation method will be available
if (LibraryA.reliesOnComplexOperation(myClassInstance)) {
myClassInstance.a = 50;
}
// I can return myClassInstance as part of the new state, and redux will convert it to a plain object
return {
...state,
myClassInstance
};
default:
return state;
}
}
Por lo tanto, este ejemplo muestra una forma de incorporar objetos con métodos en el estado de reducción como objetos simples, y luego volver a agregar los métodos para que puedan usarse dentro del reductor redux. Me encantaría escuchar los pensamientos de Dan o de cualquier otra persona sobre este patrón o cómo podría hacerse mejor. He visto publicaciones de Dan en varios lugares que el almacenamiento de objetos con métodos es una mala idea en redux, por lo que el objetivo aquí es encontrar una forma de almacenar el objeto como un objeto simple y adjuntar los métodos cuando los necesite de una manera eficiente .
No estoy seguro de que esto sea exactamente relevante (el contexto está usando Redux con React), pero encontré esta página mientras buscaba algo similar, así que escribo esto en caso de ser de interés.
Mi motivación es: tengo objetos de estado en los que me gustaría exponer una representación de ese estado en lugar del propio estado. Entonces, por ejemplo (usando Immutable.js), tengo un estado (un esquema ) que comprende uno o más campos , cada uno identificado por un identificador único y almacenado en un Mapa, más una Lista de identificadores que da el orden de campo.
Realmente no quiero escribir cosas como
state.get(''field'').get(state.get(''order'').get(nth))
para obtener el enésimo campo en cualquier lugar que no sea cerca del reductor, de modo que la representación interna esté oculta y pueda cambiarse fácilmente.
Lo que estoy haciendo actualmente (y esto es un poco de un experimento), es agregar una función en el mismo archivo que el reductor:
schema.mapVisibleState = function (state) {
return {
getOrderedFields: () => state.get(''order'').map((fid) => state.get(''fields'').get(fid)
}
}
Esto se usa en los componentes React correspondientes (junto con una función schema.bindActionCreators de motivación similar):
export const Schema = connect(
(state) => schema.mapVisibleState (state),
(dispatch) => schema.bindActionCreators (dispatch)
)(_Schema) ;
Ahora puedo acceder a los campos, en el orden correcto, dentro del componente simplemente como this.props.getOrderedFields () y no preocuparme por la representación subyacente. También tiene la propiedad agradable adicional de que es fácil hacer la inyección de dependencia de la función getOrderedFields ().
Además, dado que tengo una estructura similar para el estado del campo, puedo extender esto:
schema.mapVisibleState = function (state) {
return {
getOrderedFields: () => state.get(''order'').map((fid) => field.mapVisibleState(state.get(''fields'').get(fid)),
getNthField (nth) => field.mapVisibleState(state.get(''fields'').get(state.get(''order'').get(nth)))
}
}