@ngrx/data - 撤消乐观删除,UNDO_ONE 操作是否应该恢复 changeState?
@ngrx/data - Undoing an optimistic delete, should an UNDO_ONE action revert the changeState?
Stackblitz
在@ngrx/data 中执行乐观删除(我们的英雄 "Ant-Man")导致 changeState 更新,如下所示:
{
"entityCache": {
"Hero": {
"ids": [1, 2, 3, 5, 6],
"entities": {
"1": {
"id": 1,
"name": "Spiderman",
"power": 1
},
"2": {
"id": 2,
"name": "Thor",
"power": 5
},
"3": {
"id": 3,
"name": "Hulk",
"power": 6
},
"5": {
"id": 5,
"name": "Iron Man",
"power": 9
},
"6": {
"id": 6,
"name": "Thanos",
"power": 10
}
},
"entityName": "Hero",
"filter": "",
"loaded": true,
"loading": true,
"changeState": {
"4": {
"changeType": 2,
"originalValue": {
"id": 4,
"name": "Ant-Man",
"power": 7
}
}
}
}
}
}
使用下面的效果,当由于 http 请求错误导致删除失败时,我触发了 UNDO_ONE:
deleteError$ = createEffect(() => {
return this.actions$.pipe(
ofEntityType("Hero"),
ofEntityOp([EntityOp.SAVE_DELETE_ONE_ERROR]),
map(action => {
const id = action.payload.data.originalAction.payload.data;
const options: EntityActionOptions = {
// tried various values
}
return new EntityActionFactory().create( <-----------------------dispatch UNDO_ONE action-----------
"Hero",
EntityOp.UNDO_ONE,
id,
options
);
})
);
});
问题: 是否应该调度 UNDO_ONE 操作恢复 changeState
即删除由删除操作引起的这部分实体状态的更改?
如果是这样,您如何正确发送 UNDO_ONE 以及需要哪些参数?
我探索了 EntityActionFactory.create() method:
的数据和选项的不同值
EntityActionFactory.create<P = any>(entityName: string, entityOp: EntityOp, data?: P, options?: EntityActionOptions): EntityAction<P>
这里我正在执行乐观删除并在 SAVE_DELETE_ONE_ERROR 上通过效果调度 UNDO_ONE 操作。
当我将 UNDO_ONE 换成 UNDO_ALL 时 changeState
确实恢复到 {}
这让我有理由认为 changeState
应该恢复到 {}
因为我们要取消删除。
根据文档here,它应该:
The undo operations replace entities in the collection based on information in the changeState map, reverting them their last known server-side state, and removing them from the changeState map. These entities become "unchanged."
为了克服这个问题,您可以创建一个 metaReducer,它会在撤消操作后删除保留在 changeState 中的相关修改。这是我的实体的内容-metadata.ts 和相关的 metareducer。
import { EntityMetadataMap, EntityDataModuleConfig, EntityCache } from '@ngrx/data';
import { MetaReducer, ActionReducer, Action } from '@ngrx/store';
const entityMetadata: EntityMetadataMap = {};
const pluralNames = {};
const objectWithoutProperties = (obj, keys) => {
const target = {};
for (const i in obj) {
if (keys.indexOf(i) >= 0) { continue; }
if (!Object.prototype.hasOwnProperty.call(obj, i)) { continue; }
target[i] = obj[i];
}
return target;
};
function revertStateChanges(reducer: ActionReducer<any>): ActionReducer<any> {
return (state, action: any) => {
if (action.type.includes('@ngrx/data/undo-one')) {
// Note that you need to execute the reducer first if you have an effect to add back a failed removal
state = reducer(state, action);
const updatedChangeState = objectWithoutProperties(state[action.payload.entityName].changeState, [action.payload.data.toString()]);
const updatedState = {
...state,
[action.payload.entityName]: {
...state[action.payload.entityName],
changeState: updatedChangeState
}
};
return reducer(updatedState, action);
}
return reducer(state, action);
};
}
const entityCacheMetaReducers: MetaReducer<EntityCache, Action>[] = [revertStateChanges];
export const entityConfig: EntityDataModuleConfig = {
entityMetadata,
pluralNames,
entityCacheMetaReducers
};
可能有更好的方式来编写这段代码(特别是我处理重写 changeState 的方式 属性),但对我来说它被证明是有效的。
此外,它可能需要一些更新才能处理不同的撤消情况,因为当我编写它时,我只需要使其适用于有关删除操作的撤消操作,其中 action.payload.data 是实体编号。
Stackblitz
在@ngrx/data 中执行乐观删除(我们的英雄 "Ant-Man")导致 changeState 更新,如下所示:
{
"entityCache": {
"Hero": {
"ids": [1, 2, 3, 5, 6],
"entities": {
"1": {
"id": 1,
"name": "Spiderman",
"power": 1
},
"2": {
"id": 2,
"name": "Thor",
"power": 5
},
"3": {
"id": 3,
"name": "Hulk",
"power": 6
},
"5": {
"id": 5,
"name": "Iron Man",
"power": 9
},
"6": {
"id": 6,
"name": "Thanos",
"power": 10
}
},
"entityName": "Hero",
"filter": "",
"loaded": true,
"loading": true,
"changeState": {
"4": {
"changeType": 2,
"originalValue": {
"id": 4,
"name": "Ant-Man",
"power": 7
}
}
}
}
}
}
使用下面的效果,当由于 http 请求错误导致删除失败时,我触发了 UNDO_ONE:
deleteError$ = createEffect(() => {
return this.actions$.pipe(
ofEntityType("Hero"),
ofEntityOp([EntityOp.SAVE_DELETE_ONE_ERROR]),
map(action => {
const id = action.payload.data.originalAction.payload.data;
const options: EntityActionOptions = {
// tried various values
}
return new EntityActionFactory().create( <-----------------------dispatch UNDO_ONE action-----------
"Hero",
EntityOp.UNDO_ONE,
id,
options
);
})
);
});
问题: 是否应该调度 UNDO_ONE 操作恢复 changeState
即删除由删除操作引起的这部分实体状态的更改?
如果是这样,您如何正确发送 UNDO_ONE 以及需要哪些参数?
我探索了 EntityActionFactory.create() method:
EntityActionFactory.create<P = any>(entityName: string, entityOp: EntityOp, data?: P, options?: EntityActionOptions): EntityAction<P>
这里我正在执行乐观删除并在 SAVE_DELETE_ONE_ERROR 上通过效果调度 UNDO_ONE 操作。
当我将 UNDO_ONE 换成 UNDO_ALL 时 changeState
确实恢复到 {}
这让我有理由认为 changeState
应该恢复到 {}
因为我们要取消删除。
根据文档here,它应该:
The undo operations replace entities in the collection based on information in the changeState map, reverting them their last known server-side state, and removing them from the changeState map. These entities become "unchanged."
为了克服这个问题,您可以创建一个 metaReducer,它会在撤消操作后删除保留在 changeState 中的相关修改。这是我的实体的内容-metadata.ts 和相关的 metareducer。
import { EntityMetadataMap, EntityDataModuleConfig, EntityCache } from '@ngrx/data';
import { MetaReducer, ActionReducer, Action } from '@ngrx/store';
const entityMetadata: EntityMetadataMap = {};
const pluralNames = {};
const objectWithoutProperties = (obj, keys) => {
const target = {};
for (const i in obj) {
if (keys.indexOf(i) >= 0) { continue; }
if (!Object.prototype.hasOwnProperty.call(obj, i)) { continue; }
target[i] = obj[i];
}
return target;
};
function revertStateChanges(reducer: ActionReducer<any>): ActionReducer<any> {
return (state, action: any) => {
if (action.type.includes('@ngrx/data/undo-one')) {
// Note that you need to execute the reducer first if you have an effect to add back a failed removal
state = reducer(state, action);
const updatedChangeState = objectWithoutProperties(state[action.payload.entityName].changeState, [action.payload.data.toString()]);
const updatedState = {
...state,
[action.payload.entityName]: {
...state[action.payload.entityName],
changeState: updatedChangeState
}
};
return reducer(updatedState, action);
}
return reducer(state, action);
};
}
const entityCacheMetaReducers: MetaReducer<EntityCache, Action>[] = [revertStateChanges];
export const entityConfig: EntityDataModuleConfig = {
entityMetadata,
pluralNames,
entityCacheMetaReducers
};
可能有更好的方式来编写这段代码(特别是我处理重写 changeState 的方式 属性),但对我来说它被证明是有效的。
此外,它可能需要一些更新才能处理不同的撤消情况,因为当我编写它时,我只需要使其适用于有关删除操作的撤消操作,其中 action.payload.data 是实体编号。