Операция отмены (удаления) Redux + Normalizr

Я использую redux с normalizr для нормализации ответа от сервера, в основном следуют примеру real-world. Таким образом, редуктор entities очень прост, просто слейте ответ. Проблема, которую я сейчас имею в виду, - это операция delete. Я нашел этот номер № 21 нормального репо, но до сих пор не мог понять, как это решить. Например,

Текущее состояние

{
  entities:
    product_categories: {
      ...
      13: {
        ...
        products: ["1", "2"], <--------------- [i] Current state
        ...
      }
    },
    products: {
      1: {
        id: "1"
      }
    }
}

Нормализованный ответ

{
  ...
    product_categories: {
      ...
      13: {
        ...
        products: ["1"], <---------------- [2] Normalized result
      }
  ...
}

Как вы можете видеть, backend api возвращает все идентификаторы продуктов, принадлежащие этой категории, в этом случае "2" отделяется. Когда редуктор сущностей объединяет этот ответ, "2" все еще висит вокруг. Сейчас я просто перезагружаю страницу, но мне интересно, есть ли лучший способ справиться с этим случаем?

В редукторе entities я просто сливаю его, как в реальном примере.

return merge({}, state, action.payload.entities);

Ответ 1

Просто не беспокойтесь о том, что он там. Подумайте о своем состоянии в базе данных. Вы действительно не удаляете записи из базы данных, чтобы избежать сложных каскадов - обычно вы просто меняете свой статус в базе данных. Аналогично, с Normalizer, вместо того, чтобы действительно удалять сущности, пусть они находятся в кеше, пока пользователь не покинет страницу!

Ответ 2

Ниже приводится объяснение моего решения, за которым следует код.

Чтобы выполнить удаление, я обновил свой редуктор до дескриптора действия delete: REMOVE_ENTITY_ITEM. В действии я передаю id и name объекта, который должен быть удален.

В редукторе сначала удаляю сам объект, который находится в store.entities[entityName][entityId]. Затем мне нужно удалить его id из всех других объектов, которые могут ссылаться на него. Поскольку я использую normalizr, все мои объекты являются плоскими, и если они относятся к другому объекту, то они будут иметь свой идентификатор только в массиве. Это делает относительно простым удаление ссылки. Я просто перебираю все сущности и отфильтровываю ссылку на удаляемую сущность.

Я использую этот подход в сочетании с двумя другими подходами # 1.) обновляя приложение/состояние и # 2.) перебрасывает бит состояния сущностей, а не удаляет, а затем фильтрует отключенные элементы в пользовательском интерфейсе. Эти подходы были хорошо обсуждены здесь

const entities = (state={}, action) => {
  if(action.payload && action.payload.entities) {
    return merge( {} , state, action.payload.entities);
  }else{
    return deleteHandlingReducer(state, action)
  }
}

const deleteHandlingReducer = (state=initialSate, action) => {
  switch(action.type){
    case "REMOVE_ENTITY_ITEM":
      if (!action.meta || !action.meta.name || !action.meta.id) {
        return state;
      }else{
        let newState = Object.assign({}, state);
        if(newState[action.meta.name]){
          delete newState[action.meta.name][action.meta.id];
          Object.keys(state).map(key => {
            let entityHash = state[key];
            Object.keys(entityHash).map(entityId => {
              let entity = entityHash[entityId];
              if(entity[action.meta.name] &&
                Array.isArray(entity[action.meta.name])){
                  entity[action.meta.name] = entity[action.meta.name].
                    filter(item => item != action.meta.id)
              }
            });
          })
        }
        return newState;
      }
    default:
      return state;
  }
}

Теперь, чтобы удалить, выполните следующие действия:

store.dispatch({
  type: "REMOVE_ENTITY_ITEM",
  meta: {
    id: 1,
    name: "job_titles"
  }
});