Redux: ¿Cómo cortar el texto estándar al actualizar entidades anidadas?

Creado en 3 nov. 2015  ·  32Comentarios  ·  Fuente: reduxjs/redux

Entonces tengo esta estructura anidada bajo mi estado

state = {
   plans: [
    {title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
    {title: 'B', exercises: [{title: 'exe5'}, {title: 'exe1'},{title: 'exe2'}]}
   ]
}

Estoy tratando de crear una reducción que no mute el estado anterior, pero estoy llegando a un punto en el que paso más tiempo averiguando cómo hacer esto y luego codificando el resto de la aplicación, se está volviendo bastante frustrante.

Por ejemplo, si quisiera agregar un nuevo ejercicio vacío o actualizar uno existente, mutando los datos, simplemente haría:

state.plans[planIdx].exercises.push({})

state.plans[planIdx].exercises[exerciseIdx] = exercise

pero, ¿cuál podría ser mi mejor enfoque para hacer lo mismo en esta estructura anidada? He leído los documentos de Redux y también la parte de solución de problemas, pero lo más que logré fue actualizar un plan, donde haría:

case 'UPDATE_PLAN':
    return {
      ...state,
      plans: [
      ...state.plans.slice(0, action.idx),
      Object.assign({}, state.plans[action.idx], action.plan),
      ...state.plans.slice(action.idx + 1)
      ]
    };

¿No hay una forma más rápida de trabajar con esto? Incluso si tengo que usar bibliotecas externas, o al menos si alguien me puede explicar cómo lidiar mejor con esto ...

¡Gracias!

docs question

Comentario más útil

Sí, sugerimos normalizar sus datos.
De esta manera, no es necesario que profundice: todas sus entidades están al mismo nivel.

Entonces tu estado se vería así

{
  entities: {
    plans: {
      1: {title: 'A', exercises: [1, 2, 3]},
      2: {title: 'B', exercises: [5, 1, 2]}
     },
    exercises: {
      1: {title: 'exe1'},
      2: {title: 'exe2'},
      3: {title: 'exe3'}
    }
  },
  currentPlans: [1, 2]
}

Tus reductores pueden verse como

import merge from 'lodash/object/merge';

const exercises = (state = {}, action) => {
  switch (action.type) {
  case 'CREATE_EXERCISE':
    return {
      ...state,
      [action.id]: {
        ...action.exercise
      }
    };
  case 'UPDATE_EXERCISE':
    return {
      ...state,
      [action.id]: {
        ...state[action.id],
        ...action.exercise
      }
    };
  default:
    if (action.entities && action.entities.exercises) {
      return merge({}, state, action.entities.exercises);
    }
    return state;
  }
}

const plans = (state = {}, action) => {
  switch (action.type) {
  case 'CREATE_PLAN':
    return {
      ...state,
      [action.id]: {
        ...action.plan
      }
    };
  case 'UPDATE_PLAN':
    return {
      ...state,
      [action.id]: {
        ...state[action.id],
        ...action.plan
      }
    };
  default:
    if (action.entities && action.entities.plans) {
      return merge({}, state, action.entities.plans);
    }
    return state;
  }
}

const entities = combineReducers({
  plans,
  exercises
});

const currentPlans = (state = [], action) {
  switch (action.type) {
  case 'CREATE_PLAN':
    return [...state, action.id];
  default:
    return state;
  }
}

const reducer = combineReducers({
  entities,
  currentPlans
});

Entonces, ¿qué está pasando aquí? Primero, tenga en cuenta que el estado está normalizado. Nunca tenemos entidades dentro de otras entidades. En cambio, se refieren entre sí por ID. Por lo tanto, cada vez que cambia algún objeto, solo hay un lugar donde debe actualizarse.

En segundo lugar, observe cómo reaccionamos a CREATE_PLAN agregando una entidad apropiada en el reductor plans _y_ agregando su ID al reductor currentPlans . Esto es importante. En aplicaciones más complejas, puede tener relaciones, por ejemplo, plans reducer puede manejar ADD_EXERCISE_TO_PLAN de la misma manera agregando una nueva ID a la matriz dentro del plan. Pero si el ejercicio en sí se actualiza, _no es necesario que el plans reducer sepa eso, ya que la ID no ha cambiado_.

En tercer lugar, observe que las entidades reductoras ( plans y exercises ) tienen cláusulas especiales que vigilan action.entities . Esto es en caso de que tengamos una respuesta del servidor con "verdad conocida" que queremos actualizar todas nuestras entidades para reflejar. Para preparar sus datos de esta manera antes de enviar una acción, puede usar normalizr . Puede verlo utilizado en el ejemplo del "mundo real" en el repositorio de Redux.

Finalmente, observe cómo las entidades reductoras son similares. Es posible que desee escribir una función para generarlos. Está fuera del alcance de mi respuesta: a veces desea más flexibilidad y, a veces, desea menos repetición. Puede consultar el código de paginación en reductores de ejemplo del "mundo real" para ver un ejemplo de cómo generar reductores similares.

Ah, y usé la sintaxis { ...a, ...b } . Está habilitado en Babel etapa 2 como propuesta de ES7. Se llama “operador de extensión de objeto” y equivale a escribir Object.assign({}, a, b) .

En cuanto a las bibliotecas, puede usar Lodash (tenga cuidado de no mutar, por ejemplo, merge({}, a, b} es correcto pero merge(a, b) no lo es), updeep , react-addons-update o algo más. Sin embargo, si necesita hacer actualizaciones profundas, probablemente signifique que su árbol de estado no es lo suficientemente plano y que no utiliza la composición funcional lo suficiente. Incluso tu primer ejemplo:

case 'UPDATE_PLAN':
  return {
    ...state,
    plans: [
      ...state.plans.slice(0, action.idx),
      Object.assign({}, state.plans[action.idx], action.plan),
      ...state.plans.slice(action.idx + 1)
    ]
  };

Se puede escribir como

const plan = (state = {}, action) => {
  switch (action.type) {
  case 'UPDATE_PLAN':
    return Object.assign({}, state, action.plan);
  default:
    return state;
  }
}

const plans = (state = [], action) => {
  if (typeof action.idx === 'undefined') {
    return state;
  }
  return [
    ...state.slice(0, action.idx),
    plan(state[action.idx], action),
    ...state.slice(action.idx + 1)
  ];
};

// somewhere
case 'UPDATE_PLAN':
  return {
    ...state,
    plans: plans(state.plans, action)
  };

Todos 32 comentarios

Se recomienda normalizar JSON anidado como: https://github.com/gaearon/normalizr

Sí, sugerimos normalizar sus datos.
De esta manera, no es necesario que profundice: todas sus entidades están al mismo nivel.

Entonces tu estado se vería así

{
  entities: {
    plans: {
      1: {title: 'A', exercises: [1, 2, 3]},
      2: {title: 'B', exercises: [5, 1, 2]}
     },
    exercises: {
      1: {title: 'exe1'},
      2: {title: 'exe2'},
      3: {title: 'exe3'}
    }
  },
  currentPlans: [1, 2]
}

Tus reductores pueden verse como

import merge from 'lodash/object/merge';

const exercises = (state = {}, action) => {
  switch (action.type) {
  case 'CREATE_EXERCISE':
    return {
      ...state,
      [action.id]: {
        ...action.exercise
      }
    };
  case 'UPDATE_EXERCISE':
    return {
      ...state,
      [action.id]: {
        ...state[action.id],
        ...action.exercise
      }
    };
  default:
    if (action.entities && action.entities.exercises) {
      return merge({}, state, action.entities.exercises);
    }
    return state;
  }
}

const plans = (state = {}, action) => {
  switch (action.type) {
  case 'CREATE_PLAN':
    return {
      ...state,
      [action.id]: {
        ...action.plan
      }
    };
  case 'UPDATE_PLAN':
    return {
      ...state,
      [action.id]: {
        ...state[action.id],
        ...action.plan
      }
    };
  default:
    if (action.entities && action.entities.plans) {
      return merge({}, state, action.entities.plans);
    }
    return state;
  }
}

const entities = combineReducers({
  plans,
  exercises
});

const currentPlans = (state = [], action) {
  switch (action.type) {
  case 'CREATE_PLAN':
    return [...state, action.id];
  default:
    return state;
  }
}

const reducer = combineReducers({
  entities,
  currentPlans
});

Entonces, ¿qué está pasando aquí? Primero, tenga en cuenta que el estado está normalizado. Nunca tenemos entidades dentro de otras entidades. En cambio, se refieren entre sí por ID. Por lo tanto, cada vez que cambia algún objeto, solo hay un lugar donde debe actualizarse.

En segundo lugar, observe cómo reaccionamos a CREATE_PLAN agregando una entidad apropiada en el reductor plans _y_ agregando su ID al reductor currentPlans . Esto es importante. En aplicaciones más complejas, puede tener relaciones, por ejemplo, plans reducer puede manejar ADD_EXERCISE_TO_PLAN de la misma manera agregando una nueva ID a la matriz dentro del plan. Pero si el ejercicio en sí se actualiza, _no es necesario que el plans reducer sepa eso, ya que la ID no ha cambiado_.

En tercer lugar, observe que las entidades reductoras ( plans y exercises ) tienen cláusulas especiales que vigilan action.entities . Esto es en caso de que tengamos una respuesta del servidor con "verdad conocida" que queremos actualizar todas nuestras entidades para reflejar. Para preparar sus datos de esta manera antes de enviar una acción, puede usar normalizr . Puede verlo utilizado en el ejemplo del "mundo real" en el repositorio de Redux.

Finalmente, observe cómo las entidades reductoras son similares. Es posible que desee escribir una función para generarlos. Está fuera del alcance de mi respuesta: a veces desea más flexibilidad y, a veces, desea menos repetición. Puede consultar el código de paginación en reductores de ejemplo del "mundo real" para ver un ejemplo de cómo generar reductores similares.

Ah, y usé la sintaxis { ...a, ...b } . Está habilitado en Babel etapa 2 como propuesta de ES7. Se llama “operador de extensión de objeto” y equivale a escribir Object.assign({}, a, b) .

En cuanto a las bibliotecas, puede usar Lodash (tenga cuidado de no mutar, por ejemplo, merge({}, a, b} es correcto pero merge(a, b) no lo es), updeep , react-addons-update o algo más. Sin embargo, si necesita hacer actualizaciones profundas, probablemente signifique que su árbol de estado no es lo suficientemente plano y que no utiliza la composición funcional lo suficiente. Incluso tu primer ejemplo:

case 'UPDATE_PLAN':
  return {
    ...state,
    plans: [
      ...state.plans.slice(0, action.idx),
      Object.assign({}, state.plans[action.idx], action.plan),
      ...state.plans.slice(action.idx + 1)
    ]
  };

Se puede escribir como

const plan = (state = {}, action) => {
  switch (action.type) {
  case 'UPDATE_PLAN':
    return Object.assign({}, state, action.plan);
  default:
    return state;
  }
}

const plans = (state = [], action) => {
  if (typeof action.idx === 'undefined') {
    return state;
  }
  return [
    ...state.slice(0, action.idx),
    plan(state[action.idx], action),
    ...state.slice(action.idx + 1)
  ];
};

// somewhere
case 'UPDATE_PLAN':
  return {
    ...state,
    plans: plans(state.plans, action)
  };

Sería bueno convertir esto en una receta.

Idealmente queremos un ejemplo de tablero kanban.
Es perfecto para entidades anidadas porque los "carriles" pueden tener "tarjetas" dentro de ellos.

@ andre0799 o simplemente puede usar Immutable.js))

Idealmente queremos un ejemplo de tablero kanban.

Escribí uno . Quizás podrías bifurcarlo y ajustarlo a tu gusto.

Immutable.js no siempre es una buena solución. Vuelve a calcular el hash de cada nodo principal del estado a partir del nodo que cambió y eso se convierte en un cuello de botella en casos particulares (aunque no es un caso muy común). Entonces, idealmente, debería hacer algunos puntos de referencia antes de integrar Immutable.js en su aplicación.

Gracias @gaearon por tu respuesta, ¡gran explicación!

Por lo tanto, cuando haga CREATE_PLAN , debe crear automáticamente un ejercicio predeterminado y agregarlo. ¿Cómo debo manejar casos como este? ¿Debería llamar a 3 acciones seguidas? CREATE_PLAN, CREATE_EXERCISE, ADD_EXERCISE_TO_PLAN Desde dónde debo hacer estas llamadas, si ese es el caso?

Entonces, cuando hagas CREATE_PLAN, deberías crear automáticamente un ejercicio predeterminado y agregarlo. ¿Cómo debo manejar casos como este?

Si bien, en general, estoy a favor de que muchos reductores manejen la misma acción, puede resultar demasiado complicado para las entidades con relaciones. De hecho, sugiero modelar estos como acciones separadas.

Puede usar el middleware Redux Thunk para escribir un creador de acciones que los llame a ambos:

function createPlan(title) {
  return dispatch => {
    const planId = uuid();
    const exerciseId = uuid();

    dispatch({
      type: 'CREATE_EXERCISE',
      id: exerciseId,
      exercise: {
        id: exerciseId,
        title: 'Default'
      }
    });

    dispatch({
      type: 'CREATE_PLAN',
      id: planId,
      plan: {
        id: planId,
        exercises: [exerciseId],
        title
      }
    });
  };
}

Luego, si aplica el middleware Redux Thunk, puede llamarlo normalmente:

store.dispatch(createPlan(title));

Entonces, digamos que tengo un editor de publicaciones en el backend en algún lugar con tales relaciones (publicaciones, autores, etiquetas, archivos adjuntos, etc.).

¿Cómo hago para mostrar currentPosts similar a la matriz de claves currentPlans ? ¿Necesitaría asignar cada clave en currentPosts a su objeto correspondiente en entities.posts en la función mapStateToProps ? ¿Qué tal ordenar currentPosts ?

¿Todo esto pertenece a una composición reductora?

Yo me estoy perdiendo algo aqui...

Con respecto a la pregunta original, creo que React Immutability Helpers fue creado para ese propósito.

¿Cómo hago para mostrar currentPosts similar a la matriz de claves currentPlans? ¿Necesitaría asignar cada clave en currentPosts a su objeto correspondiente en entity.posts en la función mapStateToProps? ¿Qué tal ordenar publicaciones actuales?

Esto es correcto. Haría todo lo posible al recuperar los datos. Consulte los ejemplos de "carrito de compras" y "mundo real" que vienen con el repositorio de Redux.

Gracias, ya comencé a tener una idea después de leer Computing Derived Data en los documentos.

Revisaré esos ejemplos nuevamente. Probablemente no entendí mucho de lo que estaba pasando al principio cuando los leí.

@ andre0799

Cualquier componente connect() ed tiene dispatch inyectado como accesorio por defecto.

this.props.dispatch(createPlan(title));

Esta es una pregunta de uso que no está relacionada con este hilo. Es mejor consultar ejemplos o crear preguntas de StackOverflow para esto.

Estoy de acuerdo con Dan en normalizar los datos y aplanar la estructura del estado tanto como sea posible. Se puede poner como receta / mejor práctica en la documentación, ya que me habría ahorrado algunos dolores de cabeza.

Como cometí el error de tener un poco de profundidad en mi estado, hice esta biblioteca para ayudar a actualizar y administrar el estado profundo con Redux: https://github.com/baptistemanson/immutable-path
Tal vez me equivoqué, pero me interesaría tu opinión. Espero que ayude a alguien.

Esto fue útil. Gracias a todos.

¿Cómo haría para agregar un ejercicio a un plan, usando la misma estructura que el ejemplo del mundo real? Supongamos que al agregar un ejercicio se devuelve la entidad de ejercicio recién creada, con un campo planId . ¿Es posible agregar el nuevo ejercicio a ese plan sin tener que escribir un reductor para los planes y escuchar específicamente una acción CREATE_EXERCISE ?

Gran discusión e información aquí. Me gustaría usar normalizr para mi proyecto, pero tengo una pregunta sobre cómo guardar los datos actualizados en un servidor remoto. Principalmente, ¿hay una forma sencilla de revertir la forma normalizada a la forma anidada proporcionada por la API remota después de la actualización? Esto es importante cuando el cliente realiza cambios y necesita retroalimentarlo a la api remota donde no tiene control sobre la forma de la solicitud de actualización.

Por ejemplo: el cliente recupera los datos anidados del ejercicio -> el cliente los normaliza y los almacena en redux -> el usuario realiza cambios en los datos normalizados en el lado del cliente -> el usuario hace clic en guardar -> la aplicación cliente transforma los datos normalizados actualizados de nuevo al formulario anidado para que pueda enviarlo al servidor remoto -> el cliente lo envía al servidor

Si usara normalizr, ¿necesitaría escribir un transformador personalizado para el paso en negrita o hay una biblioteca o un método auxiliar que recomendaría para esto? Cualquier recomendación será muy apreciada.

Gracias

Hay algo llamado https://github.com/gpbl/denormalizr pero no estoy seguro de qué tan de cerca rastrea las actualizaciones de normalizr. Escribí normalizr en un par de horas para la aplicación en la que trabajé, puedes bifurcarla y agregar desnormalización 😄.

Genial, definitivamente echaré un vistazo a la desnormalización y contribuiré a tu proyecto una vez que tenga algo. Buen trabajo durante un par de horas ;-) Gracias por responderme.

Estoy en la misma situación con el cambio de datos profundamente anidados, sin embargo, encontré una solución que funciona usando immutable.js.

¿Está bien si me vinculo a una publicación de StackOverflow que hice solicitando comentarios sobre la solución aquí?

Lo estoy vinculando por ahora, elimine mi publicación o diga si no es apropiado vincularlo aquí:
http://stackoverflow.com/questions/37171203/manipulating-data-in-nested-arrays-in-redux-with-immutable-js

He visto este enfoque recomendado en el pasado. Sin embargo, este enfoque parece no funcionar bien cuando es necesario eliminar un objeto anidado. En este caso, el reductor necesitaría revisar todas las referencias al objeto y eliminarlas, una operación que sería O (n), antes de poder eliminar el objeto en sí. ¿Alguien se encontró con un problema similar y lo resolvió?

@ariofrio : ah ... estoy confundido. El punto de normalización es que los objetos _no_ se almacenan anidados y solo hay una referencia a un elemento determinado, lo que facilita la actualización de ese elemento. Ahora, si hay varias otras entidades que "hicieron referencia" a este elemento por ID, seguro, también deberían actualizarse, al igual que si las cosas no estuvieran normalizadas.

¿Tiene una preocupación específica o un escenario problemático con el que se enfrenta?

Esto es lo que quiero decir. Digamos que el estado actual se ve así:

{
  entities: {
    plans: {
      1: {title: 'A', exercises: [1, 2, 3]},
      2: {title: 'B', exercises: [5, 6]}
     },
    exercises: {
      1: {title: 'exe1'},
      2: {title: 'exe2'},
      3: {title: 'exe3'}
      5: {title: 'exe5'}
      6: {title: 'exe6'}
    }
  },
  currentPlans: [1, 2]
}

En este ejemplo, solo un plan puede hacer referencia a cada ejercicio. Cuando el usuario hace clic en "Eliminar ejercicio", el mensaje puede verse así:

{type: "REMOVE_EXERCISE", payload: 2}

Pero para implementar esto correctamente, uno necesitaría iterar sobre todos los planes, y luego todos los ejercicios dentro de cada plan, para encontrar el que hace referencia al ejercicio con id 2, para evitar una referencia pendiente. Esta es la operación O (n) que me preocupaba.

Una forma de evitar esto es incluir la identificación del plan en la carga útil de REMOVE_EXERCISE, pero en este punto, no veo la ventaja sobre el uso de anidar las estructuras. Si usamos el estado anidado en su lugar, el estado podría verse así:

{
   plans: [
    {title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
    {title: 'B', exercises: [{title: 'exe5'}, {title: 'exe6'}]}
   ]
}

Y el mensaje para eliminar el ejercicio podría verse así:

{type: "REMOVE_EXERCISE", payload: {plan_index: 0, exercise_index: 1}}

Algunos pensamientos:

  • Puede mantener una búsqueda inversa de ejercicios a planes para simplificar ese escenario. De manera similar, lo que hace la biblioteca Redux-ORM es autogenerar "a través de tablas" para relaciones de muchos tipos. Entonces, en este caso, tendría una "tabla" "PlanExercise" en su tienda, que contendría {id, planId, exerciseId} tripletes. Ciertamente un escaneo O (n), pero sencillo.
  • Una operación O (n) no es inherentemente mala. Eso depende completamente de qué tan grande sea N, el factor constante frente al término, la frecuencia con la que sucede y qué más está sucediendo en su aplicación. Iterar una lista de 10 o 15 elementos y hacer algunas comprobaciones de igualdad en un clic de botón de usuario será algo totalmente diferente a, digamos, iterar una lista de 10 millones de elementos que ingresan al sistema cada 500 ms y realizar una operación costosa para cada uno. articulo. En este caso, lo más probable es que incluso revisar miles de planes no sería un cuello de botella significativo.
  • ¿Es esto un problema de rendimiento real que está viendo o simplemente está mirando hacia adelante a posibles problemas teóricos?

En última instancia, tanto el estado anidado como el normalizado son convenciones. Hay buenas razones para usar el estado normalizado con Redux, y puede haber buenas razones para mantener su estado normalizado. Elija lo que funcione para usted :)

mi solución como esta:

function deepCombinReducer(parentReducer, subReducer) {
    return function (state = parentReducer(void(0) /* get parent reducer initial state */, {}) {
        let finalState = {...state};

        for (var k in subReducer) {
          finalState[k] = subReducer(state[k], action);
        }

       return parentReducer(finalState, action);
    };
}

const parentReducer = function(state = {}, action) {
    return state;
}

const subReducer = function(state = [], action) {
    state = Immutable.fromJS(state).toJS();
    switch(action.type) {
       case 'ADD':
          state.push(action.sub);
           return state;
       default:
          return state;
   }
}

export default combineReducers({
   parent: deepCombinReducer(parentReducer, {
       sub: subReducer
   })
})

Entonces, puedes conseguir la tienda así:

{
    parent: {
       sub: []
    }
}

dispatch({
    type: 'ADD',
    sub: '123'
});

// the store will change to:
{
    parent: {
       sub: ['123']
    }
}

@smashercosmo immutable.js con estado anidado profundo? Tengo curiosidad como

@gaearon

Nunca tenemos entidades dentro de otras entidades.

No lo entiendo. Tenemos al menos tres niveles de anidación aquí:

{
  entities: {
    plans: {
      1: {title: 'A', exercises: [1, 2, 3]},
      2: {title: 'B', exercises: [5, 1, 2]}
     },
    exercises: {
      1: {title: 'exe1'},
      2: {title: 'exe2'},
      3: {title: 'exe3'}
    }
  },
  currentPlans: [1, 2]
}

entities.plans[1] - three levels
entities.exercises[1] - three levels

Este es un objeto no anidado. Solo un nivel.

{
   plans: [1,2, 3],
   exercises: [1,2,3],
   'so forth': [1,2,3]
}

@wzup : FYI, Dan no dedica mucho tiempo a analizar los problemas de Redux en estos días; tiene suficiente trabajo en React.

El significado de "anidar" aquí es cuando los datos en sí están anidados, como este ejemplo anterior en el hilo:

{
   plans: [
    {title: 'A', exercises: [{title: 'exe1'}, {title: 'exe2'},{title: 'exe3'}]},
    {title: 'B', exercises: [{title: 'exe5'}, {title: 'exe6'}]}
   ]
}

En ese ejemplo, la única forma de acceder al ejercicio "exe6" es profundizar en la estructura, como plans[1].exercises[2] .

Me interesa la pregunta de @tmonte :

¿Cómo haría para agregar un ejercicio a un plan, usando la misma estructura que el ejemplo del mundo real? Digamos que al agregar un ejercicio se devuelve la entidad de ejercicio recién creada, con un campo planId. ¿Es posible agregar el nuevo ejercicio a ese plan sin tener que escribir un reductor para los planes y escuchar específicamente una acción CREATE_EXERCISE?

Cuando tiene muchas entidades, crear un reductor para cada entidad podría ser doloroso, pero este enfoque podría resolverlo. Hasta ahora no he encontrado una solución.

Presonnaly uso mergeWith lugar de merge para mayor flexibilidad:

import mergeWith from 'lodash/mergeWith';

// Updates an entity cache in response to any action with `entities`.
function entities(state = {}, action) {
  // Here where we STORE or UPDATE one or many entities
  // So check if the action contains the format we will manage
  // wich is `payload.entities`
  if (action.payload && action.payload.entities) {
    // if the entity is already in the store replace
    // it with the new one and do not merge. Why?
    // Assuming we have this product in the store:
    //
    // products: {
    //   1: {
    //     id: 1,
    //     name: 'Awesome product name',
    //     rateCategory: 1234
    //   }
    // }
    //
    // We will updated with
    // products: {
    //   1: {
    //     id: 1,
    //     name: 'Awesome product name',
    //   }
    // }
    //
    // The result if we were using `lodash/merge`
    // notice the rate `rateCategory` hasn't changed:
    // products: {
    //   1: {
    //     id: 1,
    //     name: 'Awesome product name',
    //     rateCategory: 1234
    //   }
    // }
    // for this particular use case it's safer to use
    // `lodash/mergeWith` and skip the merge
    return mergeWith({}, state, action.payload.entities, (oldD, newD) => {
      if (oldD && oldD.id && oldD.id === newD.id) {
        return newD;
      }
      return undefined;
    });
  }

  // Here you could register other handlers to manipulate 
  // the entities
  switch (action.type) {
    case ActionTypes.SOME_ACTION:
      // do something;
    default:
      return state;
  }
}

const rootReducer = combineReducers({
  entities,
});
export default rootReducer;
¿Fue útil esta página
0 / 5 - 0 calificaciones