Операция отмены (удаления) 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"
}
});