angular-architects/ngrx-toolkit

`withUndoRedo` and store with side-effects

Opened this issue · 5 comments

Hi,
I have a use case where I am using the undo-redo feature, but when I add an entity I also execute a side-effect (e.g. HTTP request). I am now considering how to implement the reverse side effect when undoing the state (which is an effect for the deletion of an entity).

My idea is that if I could specify a reverse action in an action, then this reverse action could be called by the undo-redo when undoing the state. This however would require undo-redo to register a stack of previous and future actions (like a list of diffs) instead of holding copies of the state and to be able to observe when an action is called.

Hey, so how would that look like? Could you come with an example pseudo example?

@rainerhahnekamp I am trying to make this work so far I have sth like this:

withUndoRedo({
  actionsMap: {
    addEntity: 'removeEntity',
    removeEntity: 'addEntity',
    updateEntity: {
      actionName: 'updateEntity',
      payloadFn: ({ oldEntity, entity }) => ({ oldEntity: entity, entity: oldEntity }),
    },
  },
}),

If the payload is the same type you can provide just a reversed action name, or provide a payload mapper, if it has other payload types you have to provide the mapper.

Then undo-redo listens when the method (action) is called and adds it to the stack, when we call undo() then we take last action from the stack, use the actionsMap to figure out reverse one and run in on the store

@rainerhahnekamp
I wanted to get info when the action is called by implementing something like this:

withHooks({
  onInit(store) {
    Object.keys(normalized.actionsMap).forEach((action: keyof Input['methods']) => {
      const originalActionCreator = (store as Record<keyof Input['methods'], BasicActionCreator>)[action];
      (store as Record<keyof Input['methods'], BasicActionCreator>)[action] = (payload: Payload) => {
        originalActionCreator(payload);
        if (!skip) {
          undoStack.push({ actionName: action as string, payload });
          redoStack.splice(0);
        }
      };
    });
  },
})

But the replaced fn is not called for some reason.

Do you have some other idea how this could be implemented?

Ah, i figured it out, these functions are kind of immutable and are overridden later. I have a working solution that overrides the action creator functions with the use of withMethods, but this undoRedo feature is very different than the one in the library. Would you be interested in adding it here as a second undoRedo variant or should I keep it to myself?

I'd be careful and not patch the internals that much. We can keep this issue opened for 60 days and check if other developers require it as well.