@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 是实体编号。