【问题标题】:How to use more than one NGRX action that does the same thing following "action hygiene" guideline?如何按照“动作卫生”指南使用多个执行相同操作的 NGRX 动作?
【发布时间】:2019-06-22 18:07:54
【问题描述】:

如果我有两个或多个 NGRX 操作执行相同的操作,但在我的应用程序的不同部分触发,我可以使用一个操作来执行此操作,同时区分我的 NGRX 日志中的操作字符串吗?我问是因为在编写动作并尝试遵循“动作卫生”原则时,动作应该特定于事件并且可以在一年后读取并告诉开发人员确切地在应用程序中的哪个位置触发了动作,我想限制为尽可能多的 NGRX 样板。

例如:我有一个“AddTodo”操作。此操作可以在我的应用程序中的两个位置执行 - 一个在“待办事项列表”页面上,另一个在“待办事项详细信息”页面上。每个动作对于触发它们的页面都是唯一的,但它们做的事情完全相同;他们向我的状态对象添加了一个新的 Todo 项。

const TODO_LIST = '[Todo List]';
const TODO_DETAILS = '[Todo Details]';

export const TODO_LIST_ADD = `${TODO_LIST} Add`;
export const TODO_DETAILS_ADD = `${TODO_DETAILS} Add`;

export class TodoListAdd implements Action {
    readonly type = TODO_LIST_ADD;
    constructor(public payload: Todo) { }
}

export class TodoDetailsAdd implements Action {
    readonly type = TODO_DETAILS_ADD;
    constructor(public payload: Todo) { }
}

export function todoReducer(state = initialState, action: Action): TodoState {
    switch (action.type) {
        case actions.TODO_LIST_ADD:
        case actions.TODO_DETAILS_ADD:
            return {
                ...state,
                todo: (action as actions.TodoListAdd | actions.TodoDetailsAdd).payload,
            }
        default:
            return state;
    }
}

如果想保持动作卫生,这是唯一的方法吗?有两个操作完全相同,唯一的区别是在 NGRX 事件日志中区分它们的字符串类型。我希望有一种方法可以通过耦合动作创建者来减少样板的数量,而不会失去类型的特异性。

谢谢,

【问题讨论】:

  • 从这个角度来看,连续使用几个“案例”来创建这个“失败”并没有什么坏处,消除代码重复是一件好事。但在目前的情况下,甚至从意义的角度来看,合并这两个动作会更好吗?例如添加到“ADD_TODO”中,如果它是相同的实体和状态的相同部分,则从哪个位置添加 todo 并不重要。
  • @AmirArbabian 从哪个地方添加待办事项并不重要...是的,是的。正如 OP 所说,将动作与唯一事件联系起来让应用程序在一年后变得可读,并告诉开发人员确切地在应用程序中的哪个位置触发了动作

标签: angular redux ngrx


【解决方案1】:

是的,如果您遵循良好的行为卫生,这是唯一的方法。

但是,您可以按照此处https://medium.com/javascript-everyday/reusable-action-creators-with-context-2a31dca98192 的描述为您的操作提供上下文。

【讨论】:

    【解决方案2】:

    经过几次尝试在接受的答案中实现类似的东西,并且在:

    https://indepth.dev/posts/1407/force-good-action-hygiene-and-write-less-actions-in-ngrx-with-the-prepared-events-pattern

    我找到了一个更适合我的替代解决方案,并认为最好将其发布在此处,以防将来对任何人有所帮助。我从 ngrx GitHub 上的一个关于处理此类案例的问题中获得了这个想法:

    https://github.com/ngrx/platform/issues/2902#issuecomment-771194540

    在解决了一些 eslint/typescript 错误后,我想出了以下解决方案:

    1. 创建一些助手来处理具有特殊kind 属性的操作。此属性可用于整个应用中的各种操作,使 Good Action Hygiene 更加干燥:
    export type CategoryAction = Action & { kind: string; };
    
    export function hasKind({ kind }: { kind: string }) {
      return {
        kind
      };
    }
    
    export function ofKind<T extends CategoryAction>(kind: string): OperatorFunction<Action, T> {
      return pipe(
        filter(action => ((action as unknown) as CategoryAction)?.kind === kind),
        map(action => action as T)
      );
    }
    
    1. 为您希望在整个应用中的多个位置使用的每种操作类型创建一个 mixin:
    export function hasNewTodo({ todo }: { todo: Todo }) {
      return {
        todo,
        ...hasKind({ kind: 'ADD_TODO' })
      };
    }
    
    1. 添加将任何此类操作转换为单个操作的效果:
    // In your actions file
    const addTodo = createAction(
      '[Todo API] Create Todo',
      props<{ todo: Todo }>()
    );
    
    // In your effects class
      addTodo$ = createEffect(() => {
        return this.actions$.pipe(
          ofKind<CategoryAction & { todo: Todo }>('ADD_TODO'),
          concatMap(action => {
            return of(fromActions.addTodo({ todo: action.todo }));
          })
        );
      });
    
    1. 为效果中的动作添加减速器:
    const todoReducer = createReducer(
      initialState,
      on(fromActions.addTodo, (state, action) => {
        return {
          ...state,
          todos: [
            ...state.todos,
            action.todo
          ]
        };
      })
    );
    
    1. 在整个应用中添加您需要的任何操作:
    // Near your TodoList component
    export const addTodo = createAction(
      '[Todo List] Add Todo',
      (payload: { todo: Todo}) => ({
        ...hasNewTodo({ todo: payload.todo })
      })
    );
    
    // Near your TodoDetails component
    export const addTodo = createAction(
      '[Todo Details] Add Todo',
      (payload: { todo: Todo}) => ({
        ...hasNewTodo({ todo: payload.todo })
      })
    );
    

    优点:

    • 最少的额外代码允许在整个应用中执行类似操作
    • 使用普通的 ngrx 构造,而不需要全部覆盖/替换它们
    • 遵循开/关原则:添加此类新动作不需要您编辑效果或减速器,您只需添加一个动作

    缺点:

    • 每个类别都需要额外的“翻译”效果
    • 每次添加待办事项时,日志中都会针对该操作显示两个不同的操作。

    【讨论】:

      猜你喜欢
      • 2017-03-29
      • 2020-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多