Redux: Como cortar o boilerplate ao atualizar entidades aninhadas?

Criado em 3 nov. 2015  ·  32Comentários  ·  Fonte: reduxjs/redux

Então, eu tenho essa estrutura aninhada em meu estado

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

Estou tentando criar uma redução que não mude o estado anterior, mas está chegando a um ponto em que estou gastando mais tempo descobrindo como fazer isso do que codificando o resto do aplicativo, está ficando muito frustrante.

Por exemplo, se eu quisesse adicionar um novo exercício vazio ou atualizar um existente, alterando os dados, eu simplesmente faria:

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

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

mas qual poderia ser minha melhor abordagem para fazer o mesmo nessa estrutura aninhada? Eu li a documentação do Redux e também a parte de solução de problemas, mas o máximo que consegui foi atualizar um plano, onde faria:

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)
      ]
    };

Não existe uma maneira mais rápida de trabalhar com isso? Mesmo que eu tenha que usar bibliotecas externas, ou pelo menos se alguém puder me explicar como lidar melhor com isso ...

Obrigado!

docs question

Comentários muito úteis

Sim, sugerimos normalizar seus dados.
Desta forma, você não precisa ir “fundo”: todas as suas entidades estão no mesmo nível.

Então, seu estado pareceria

{
  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]
}

Seus redutores podem parecer

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
});

Então, o que está acontecendo aqui? Primeiro, observe que o estado é normalizado. Nunca temos entidades dentro de outras entidades. Em vez disso, eles se referem uns aos outros por IDs. Portanto, sempre que algum objeto muda, há apenas um único lugar onde ele precisa ser atualizado.

Em segundo lugar, observe como reagimos a CREATE_PLAN adicionando uma entidade apropriada no plans redutor _and_ adicionando seu ID ao currentPlans redutor. Isso é importante. Em aplicativos mais complexos, você pode ter relacionamentos, por exemplo, plans reducer pode lidar com ADD_EXERCISE_TO_PLAN da mesma maneira, acrescentando um novo ID ao array dentro do plano. Mas se o próprio exercício for atualizado, _não há necessidade do redutor plans saber disso, pois o ID não mudou_.

Terceiro, observe que os redutores de entidades ( plans e exercises ) têm cláusulas especiais observando action.entities . Isso ocorre no caso de termos uma resposta do servidor com “verdade conhecida” que queremos atualizar todas as nossas entidades para refletir. Para preparar seus dados dessa maneira antes de despachar uma ação, você pode usar o normalizr . Você pode vê-lo usado no exemplo do “mundo real” no repositório Redux.

Finalmente, observe como os redutores de entidades são semelhantes. Você pode querer escrever uma função para gerá-los. Está fora do escopo da minha resposta - às vezes você quer mais flexibilidade e às vezes você quer menos clichê. Você pode verificar o código de paginação em redutores de exemplo do “mundo real” para ver um exemplo de geração de redutores semelhantes.

Ah, e usei a sintaxe { ...a, ...b } . Está habilitado no estágio 2 de Babel como proposta ES7. É chamado de “operador de propagação de objeto” e equivalente a escrever Object.assign({}, a, b) .

Quanto às bibliotecas, você pode usar o Lodash (porém, tenha cuidado para não sofrer mutações, por exemplo, merge({}, a, b} está correto, mas merge(a, b) não está), updeep , -addons-update ou qualquer outra coisa. No entanto, se você precisar fazer atualizações profundas, provavelmente significa que sua árvore de estado não é plana o suficiente e que você não utiliza composição funcional o suficiente. Mesmo seu primeiro exemplo:

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)
    ]
  };

pode ser escrito 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 comentários

É recomendado normalizar JSON aninhado como: https://github.com/gaearon/normalizr

Sim, sugerimos normalizar seus dados.
Desta forma, você não precisa ir “fundo”: todas as suas entidades estão no mesmo nível.

Então, seu estado pareceria

{
  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]
}

Seus redutores podem parecer

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
});

Então, o que está acontecendo aqui? Primeiro, observe que o estado é normalizado. Nunca temos entidades dentro de outras entidades. Em vez disso, eles se referem uns aos outros por IDs. Portanto, sempre que algum objeto muda, há apenas um único lugar onde ele precisa ser atualizado.

Em segundo lugar, observe como reagimos a CREATE_PLAN adicionando uma entidade apropriada no plans redutor _and_ adicionando seu ID ao currentPlans redutor. Isso é importante. Em aplicativos mais complexos, você pode ter relacionamentos, por exemplo, plans reducer pode lidar com ADD_EXERCISE_TO_PLAN da mesma maneira, acrescentando um novo ID ao array dentro do plano. Mas se o próprio exercício for atualizado, _não há necessidade do redutor plans saber disso, pois o ID não mudou_.

Terceiro, observe que os redutores de entidades ( plans e exercises ) têm cláusulas especiais observando action.entities . Isso ocorre no caso de termos uma resposta do servidor com “verdade conhecida” que queremos atualizar todas as nossas entidades para refletir. Para preparar seus dados dessa maneira antes de despachar uma ação, você pode usar o normalizr . Você pode vê-lo usado no exemplo do “mundo real” no repositório Redux.

Finalmente, observe como os redutores de entidades são semelhantes. Você pode querer escrever uma função para gerá-los. Está fora do escopo da minha resposta - às vezes você quer mais flexibilidade e às vezes você quer menos clichê. Você pode verificar o código de paginação em redutores de exemplo do “mundo real” para ver um exemplo de geração de redutores semelhantes.

Ah, e usei a sintaxe { ...a, ...b } . Está habilitado no estágio 2 de Babel como proposta ES7. É chamado de “operador de propagação de objeto” e equivalente a escrever Object.assign({}, a, b) .

Quanto às bibliotecas, você pode usar o Lodash (porém, tenha cuidado para não sofrer mutações, por exemplo, merge({}, a, b} está correto, mas merge(a, b) não está), updeep , -addons-update ou qualquer outra coisa. No entanto, se você precisar fazer atualizações profundas, provavelmente significa que sua árvore de estado não é plana o suficiente e que você não utiliza composição funcional o suficiente. Mesmo seu primeiro exemplo:

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)
    ]
  };

pode ser escrito 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)
  };

Seria bom transformar isso em uma receita.

Idealmente, queremos um exemplo de placa kanban.
É perfeito para entidades aninhadas porque “pistas” podem ter “cartões” dentro delas.

@ andre0799 ou você pode apenas usar Immutable.js))

Idealmente, queremos um exemplo de placa kanban.

Eu escrevi um . Talvez você possa fazer um fork e ajustar ao seu gosto.

Immutable.js nem sempre é uma boa solução. Ele recalcula o hash de cada nó pai de estado, começando do nó que você alterou e que se torna o gargalo em casos específicos (embora isso não seja um caso muito comum). Portanto, idealmente, você deve fazer alguns benchmarks antes de integrar Immutable.js em seu aplicativo.

Obrigado @gaearon pela sua resposta, ótima explicação!

Portanto, ao fazer CREATE_PLAN você deve criar automaticamente um exercício padrão e adicioná-lo a ele. Como devo lidar com casos como este? Devo então chamar 3 ações seguidas? CREATE_PLAN, CREATE_EXERCISE, ADD_EXERCISE_TO_PLAN De onde devo fazer essas chamadas, se for esse o caso?

Portanto, quando você faz CREATE_PLAN, deve criar automaticamente um exercício padrão e adicioná-lo. Como devo lidar com casos como este?

Embora geralmente eu seja a favor de muitos redutores tratando da mesma ação, pode ficar muito complicado para entidades com relacionamentos. Na verdade, estou sugerindo modelá-los como ações separadas.

Você pode usar o middleware Redux Thunk para escrever um criador de ação que chame os dois:

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
      }
    });
  };
}

Então, se você aplicar o middleware Redux Thunk, poderá chamá-lo normalmente:

store.dispatch(createPlan(title));

Digamos que eu tenha um editor de postagem no backend em algum lugar com esses relacionamentos (postagens, autores, tags, anexos, etc.)

Como faço para exibir currentPosts semelhante ao currentPlans array de chaves? Eu precisaria mapear cada chave em currentPosts para seu objeto correspondente em entities.posts na função mapStateToProps ? Que tal classificar currentPosts ?

Tudo isso pertence a uma composição redutora?

Estou faltando alguma coisa aqui ...

Em relação à pergunta original, acredito que o React Immutability Helpers foi criado para esse fim

Como faço para exibir currentPosts semelhantes à matriz de chaves currentPlans? Eu precisaria mapear cada chave em currentPosts para seu objeto correspondente em entity.posts na função mapStateToProps? Que tal classificar currentPosts?

Isto está certo. Você faria tudo ao recuperar os dados. Por favor, consulte os exemplos de "carrinho de compras" e "mundo real" que vêm com o repositório Redux.

Obrigado, já comecei a ter uma ideia depois de ler Computing Derived Data na docs.

Vou verificar esses exemplos novamente. Provavelmente não entendi muito o que estava acontecendo no início, quando os li.

@ andre0799

Qualquer componente connect() ed tem dispatch injetado como um prop por padrão.

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

Esta é uma questão de uso que não está relacionada a este tópico. É melhor consultar exemplos ou criar perguntas StackOverflow para isso.

Eu concordo com Dan em normalizar os dados e nivelar a estrutura do estado tanto quanto possível. Pode ser colocado como uma receita / melhor prática na documentação, pois teria me salvado de algumas dores de cabeça.

Como cometi o erro de ter um pouco de profundidade no meu estado, criei esta lib para ajudar a readaptar e gerenciar o estado profundo com Redux: https://github.com/baptistemanson/immutable-path
Talvez eu tenha entendido tudo errado, mas estou interessado em seus comentários. Espero que ajude alguém.

Isso foi útil. Obrigado a todos.

Como você adicionaria um exercício a um plano, usando a mesma estrutura do exemplo do mundo real? Digamos que adicionar um exercício retornou a entidade de exercício recém-criada, com um campo planId . É possível adicionar o novo exercício a esse plano sem ter que escrever um redutor para os planos e ouvir especificamente uma ação CREATE_EXERCISE ?

Grande discussão e informações aqui. Gostaria de usar o normalizr para meu projeto, mas tenho uma dúvida sobre como salvar os dados atualizados de volta em um servidor remoto. Principalmente, há uma maneira simples de reverter a forma normalizada de volta à forma aninhada fornecida pela API remota após a atualização? Isso é importante quando o cliente faz alterações e precisa alimentá-lo de volta para a API remota, onde ele não tem controle sobre o formato da solicitação de atualização.

Por exemplo: o cliente busca os dados do exercício aninhados -> o cliente normaliza e armazena em redux -> o usuário faz alterações nos dados normalizados no lado do cliente -> o usuário clica em salvar -> o aplicativo cliente transforma os dados normalizados atualizados de volta para a forma aninhada para que ele possa enviá-lo ao servidor remoto -> o cliente envia ao servidor

Se eu usasse o normalizr, precisaria escrever um transformador personalizado para a etapa em negrito ou há uma biblioteca ou método auxiliar que você recomendaria para isso? Quaisquer recomendações seriam muito apreciadas.

obrigado

Existe algo chamado https://github.com/gpbl/denormalizr, mas não tenho certeza de quão perto ele acompanha as atualizações do normalizr. Escrevi normalizr em algumas horas para o aplicativo em que trabalhei. Você está convidado a fazer um fork e adicionar desnormalização 😄.

Legal, definitivamente darei uma olhada na desnormalização e contribuirei com o seu projeto assim que tiver algo. Excelente trabalho por algumas horas ;-) Obrigado por entrar em contato comigo.

Estou meio na mesma situação com a alteração de dados profundamente aninhados, no entanto, encontrei uma solução de trabalho usando immutable.js.

Tudo bem se eu vincular a uma postagem StackOverflow que fiz pedindo feedback sobre a solução aqui?

Estou vinculando-o por enquanto, exclua minha postagem ou diga se não é apropriado vincular aqui:
http://stackoverflow.com/questions/37171203/manipulating-data-in-nested-arrays-in-redux-with-immutable-js

Já vi essa abordagem ser recomendada no passado. No entanto, essa abordagem parece não funcionar bem quando um objeto aninhado precisa ser removido. Nesse caso, o redutor precisaria examinar todas as referências ao objeto e removê-las, uma operação que seria O (n), antes de poder remover o próprio objeto. Alguém se deparou com um problema semelhante e o resolveu?

@ariofrio : ah ... estou confuso. O ponto de normalização é que os objetos _não_ são armazenados aninhados e há apenas uma referência a um determinado item, facilitando a atualização desse item. Agora, se houver várias outras entidades que "referenciaram" este item por ID, claro, elas também precisarão ser atualizadas, como se as coisas não tivessem sido normalizadas.

Você tem uma preocupação específica ou um cenário problemático com o qual está lidando?

Aqui está o que quero dizer. Digamos que o estado atual seja:

{
  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]
}

Neste exemplo, cada exercício só pode ser referenciado por um plano. Quando o usuário clica em "Remover exercício", a mensagem pode ser semelhante a esta:

{type: "REMOVE_EXERCISE", payload: 2}

Mas para implementar isso corretamente, seria necessário iterar sobre todos os planos e, em seguida, todos os exercícios dentro de cada plano, para encontrar aquele que referencia o exercício com id 2, a fim de evitar uma referência pendente. Esta é a operação O (n) que me preocupou.

Uma maneira de evitar isso é incluir a id do plano na carga útil de REMOVE_EXERCISE, mas, neste ponto, não vejo vantagem sobre o aninhamento das estruturas. Se usarmos o estado aninhado, o estado pode ser semelhante a:

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

E a mensagem para remover o exercício pode ser assim:

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

Algumas reflexões:

  • Você pode manter uma pesquisa reversa de exercícios para planos para simplificar esse cenário. Da mesma forma, o que a biblioteca Redux-ORM faz é gerar automaticamente "por meio de tabelas" para relações de muitos tipos. Portanto, neste caso, você teria uma "tabela" "PlanExercise" em sua loja, que conteria {id, planId, exerciseId} trigêmeos. Certamente uma varredura O (n), mas direta.
  • Uma operação O (n) não é inerentemente uma coisa ruim. Isso depende inteiramente de quão grande é N, do fator constante na frente do termo, da frequência com que isso acontece e do que mais está acontecendo em sua aplicação. Iterar sobre uma lista de 10 ou 15 itens e fazer algumas verificações de igualdade com um clique de botão do usuário será uma coisa totalmente diferente do que, digamos, iterar uma lista de 10 milhões de itens que entram no sistema a cada 500 ms e realizar alguma operação cara para cada um item. Nesse caso, as chances são de que mesmo verificar milhares de planos não seria um gargalo significativo.
  • É uma preocupação real de desempenho que você está vendo ou apenas olhando para possíveis problemas teóricos?

Em última análise, os estados aninhado e normalizado são convenções. Existem boas razões para usar o estado normalizado com Redux e pode haver boas razões para manter seu estado normalizado. Escolha o que funciona para você :)

minha solução assim:

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

Então, você pode obter a loja assim:

{
    parent: {
       sub: []
    }
}

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

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

@smashercosmo immutable.js com estado aninhado profundo? Estou curioso como

@gaearon

Nunca temos entidades dentro de outras entidades.

Eu não entendo. Temos pelo menos três níveis de aninhamento aqui:

{
  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 é um objeto não aninhado. Apenas um nível.

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

@wzup : Para sua informação, Dan não gasta muito tempo olhando para os problemas do Redux atualmente - ele já tem o suficiente trabalhando em React.

O significado de "aninhamento" aqui é quando os próprios dados são aninhados, como este exemplo anterior no thread:

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

Nesse exemplo, a única maneira de acessar o exercício "exe6" é cavar na estrutura, como plans[1].exercises[2] .

Estou interessado na pergunta de @tmonte :

Como você adicionaria um exercício a um plano, usando a mesma estrutura do exemplo do mundo real? Digamos que adicionar um exercício retornou a entidade de exercício recém-criada, com um campo planId. É possível adicionar o novo exercício a esse plano sem ter que escrever um redutor para os planos e ouvir especificamente uma ação CREATE_EXERCISE?

Quando você tem muitas entidades, criar um redutor para cada entidade pode ser doloroso, mas essa abordagem pode resolvê-lo. Não encontrei uma solução para isso até agora.

Presonalmente, uso mergeWith vez de merge para obter mais flexibilidade:

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;
Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

caojinli picture caojinli  ·  3Comentários

ramakay picture ramakay  ·  3Comentários

ms88privat picture ms88privat  ·  3Comentários

vslinko picture vslinko  ·  3Comentários

parallelthought picture parallelthought  ·  3Comentários