【问题标题】:Ngrx: How to simplify the definition of actions?Ngrx:如何简化动作的定义?
【发布时间】:2022-01-07 12:50:10
【问题描述】:

有什么方法可以简化动作的定义,让我们不需要一遍遍地重复代码?

问题

使用 Ngrx,我们必须始终使用操作。在我的情况下,大多数时候每个动作确实有一个“Do”-Action、一个“Success”-Action 和一个“Failure”-Action。在我的情况下,这些操作中的每一个都具有相同的范围(例如“[Authentication]”)和相同的标签(例如“登录用户”)。

当我想为用户身份验证创建操作时,我会按如下方式创建这些操作:

export const loginUser = createAction(
    '[Authentication] DO: Login user',
    props<{
      username: string;
      password: string;
    }
);

export const loginUserSuccess = createAction(
    '[Authentication] SUCCESS: Login user',
    props<{
      token: string;
    }
);

export const loginUserFailure = createAction(
    '[Authentication] FAILURE: Login user',
    props<{
      error: string;
    }
);

如你所见,有很多重复:

  • 三个“[Authentication]”用于同一范围
  • 三个“登录用户”用于同一种操作
  • “DO”、“SUCCESS”、“FAILURE”部分在所有操作中都相同

有没有什么方法可以创建一个工厂,用更少的冗余代码来简化动作创建?

我目前的解决方案

到目前为止,我发现的唯一简化是创建一个范围相同的动作类,并将同一类动作的所有动作包装在一个对象中:

store.functions.ts

import {createAction, props} from '@ngrx/store';

export function createDoActionType(scope: string, label: string) {
  return `[${scope}] DO: ${label}`;
}

export function createSuccessActionType(scope: string, label: string) {
  return `[${scope}] SUCCESS: ${label}`;
}

export function createErrorAction(scope: string, label: string) {
  return createAction(
    `[${scope}] FAILURE: ${label}`,
    props<ErrorProps>()
  );
}

export interface ErrorProps {
  error: string;
}

authentication.actions.ts

export class AuthenticationActions {
  private static scope = 'Authentication';

    static loginUser = ((scope: string, label: string) => ({
    do: createAction(
      createDoActionType(scope, label),
      props<{
        userEmail: string;
        password: string;
      }>()
    ),
    success: createAction(
      createSuccessActionType(scope, label),
      props<{
        userID: number;
        token: string;
      }>()
    ),
    failure: createErrorAction(scope, label)
  }))(AuthenticationActions.scope, 'Login user');
}

我认为我的解决方案只是一种解决方法。虽然它为我节省了 10 行代码,但它并不是最优的......

【问题讨论】:

  • 我对这种情况很好奇,我首先想到的是像你一样,但我仍然想知道是否有替代解决方案
  • 我个人喜欢使用 redux 工具包结合鸭子模式。 redux-toolkit.js.org 。因此,我没有创建一堆动作,而是使用 createSlice() 创建切片。节省大量样板文件
  • 我和你有同样的问题,但我建议为每个操作创建新操作,以便随着项目的增长更好地调试。您创建的简化可能很好,但我不确定它是否节省了很多。观看有关该主题的视频:youtube.com/watch?v=JmnsEvoy-gY
  • 我找到了一种减少代码重复的方法。我希望会有更好的解决方案...@AliF50 我不认为我的解决方案会使调试变得更加困难,因为我仍然像以前一样初始化操作,但冗余代码更少

标签: angular typescript ngrx ngrx-store


【解决方案1】:

我的公司开源了一个库,只是为了将其作为 ngrx 的扩展来处理。

https://github.com/acandylevey/ngrx-http-tracking

使用createTrackingActions,创建了 3 个操作:

  • 加载 -> 请求
  • 已加载
  • 失败
const actionNameSpace = 'Users';

(...)

//                                         Request type     Response Type
//                                                \/          \/   
export const fetchUsers = createTrackingActions<UserRequest, User[]>(
  actionNameSpace,
  'fetchUsers'
);

与它相结合,我们有 createTrackingEffect 函数,它接受由 createTrackingActions 创建的动作并连接 3 个下划线动作

import { UserApiService } from './user-api.service';
import * as UserActions from './user.actions';

(...)

fetchUsers$ = createTrackingEffect(
    this.actions$,
    UserActions.fetchUsers,
    this.userApi.fetchUsers,
    'Could not load users' // there is a final optional function argument here to allow further function calls after the action is processed.
  );

并且reducer也被简化了,因为通常只需要处理loaded这个动作:

const usersReducer = createReducer(
  initialState,
  on(UserActions.fetchUsers.loaded, (state, { payload }) =>
    userssAdapter.setAll(payload, { ...state })
  )
);

跟踪结果

默认情况下,所有操作都带有一个全局标记,然后可以在全局级别轻松拦截,以提供全局错误处理或全局加载微调器(如果有任何操作正在等待 http 请求)。

httpTrackingFacade也是lib提供的

isLoaded$ = this.httpTracker.isLoaded(UserActions.fetchUsers);
 isLoading$ = this.httpTracker.isLoading(UserActions.fetchUsers);

 isGloballyLoading$ = this.httpTrackingFacade.getGlobalLoading();
 globalErrors$ = this.httpTrackingFacade.getGlobalErrors();

并跟踪个人操作有

从外观触发请求

 fetchUsers() {
    this.store.dispatch(UserActions.fetchUsers.loading());
    return UserActions.fetchUsers;
  }

跟踪它

  this.httpTrackingFacade
      .getResolved(this.userFacade.fetchUsers())
      .subscribe((result) => {
        console.log(`The result from the fetch users call was: {result}`);
      });

【讨论】:

  • 有趣的解决方案!但我正在寻找一种方法来简化任何目的的动作创建。据我所知,您的解决方案仅适用于 http 请求,对吗?如果是这样,您可以(也许)找到一种方法来更改它,使其适用于所有类型的操作。
  • 它适用于 http,但它可以用于任何总是会有预期响应的操作。对于没有预期响应的动作,标准动作更合适,我猜它足够精简
猜你喜欢
  • 2022-10-05
  • 1970-01-01
  • 2020-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多