更多 @ngrx/effects

More @ngrx/effects

asked a question 不久前围绕 @ngrx/effects 图书馆,但回复并没有完全解决我的问题。

我现在很高兴我理解了动作和效果是如何相互关联的,但我仍然不确定它们执行什么功能。 example app 在某种程度上帮助了我,但似乎效果正在做所有繁重的工作:例如,loadCollection$ 效果似乎正在加载所有资源;随后发送 AddBookSuccessAction 似乎将这本书添加到 collection。

在此应用中,AddBookAction 似乎在减速器中不执行任何操作,但效果是加载资源并调度成功操作。

我想我真正想知道的是关注点分离:动作应该做什么,效果应该做什么?

当您将 @ngrx/effects 引入您的应用程序时,您需要有一点不同的想法。以登录组件和用户状态为例

@Component({})
class LoginComponent {

  login(form: any) { // implementation }
}

@Component({})
class AppComponent {
  user: Observable<User>;

  constructor(store: Store<AppState>) {
    this.user = store.select('user');
  }
}

如果不使用效果,您的 login 实现可能类似于

login(form: any) {
  this.authService.login(new Credentials(form))
    .map(res => res.json() as User)
    .subscribe(user => {
      this.store.dispatch({ type: UPDATE_USER, payload: user });
    });
}

当调用 dispatch 时,reducer 使用该操作来更新用户状态。所以这个动作并没有真正任何事情。 reducer 可以根据操作使用有效负载来更新状态。订阅(在本例中)AppComponent 将获得更新的用户。

引入效果时,主要区别在于 login 的处理方式。 USER_UPDATED 操作 用户减速器没有任何变化。所发生的变化是您引入了另一个操作,该操作由 login 方法调度。所以现在你的新 login 就是

login(form: any) {
  this.store.dispatch({ type: LOGIN, payload: form })
}

现在效果的目的是处理通常在 login 中处理的内容。它会监听 LOGIN 动作,然后将自己转换为 USER_UPDATED 动作。

@Injectable()
class LoginEffects {
  constructor(private actions: Actions, private authService: AuthService) {}

  @Effect
  login$ = this.actions
    .ofType(LOGIN)
    .map(action => new Credentials(action.payload))
    .switchMap(credentials => {
      return this.authService.login(credentials)
        .map(res => res.json() as User)
        .map({ type: USER_UPDATED, payload: user })
    });
}

你可以看到,从 LOGIN 动作中,我们得到了凭据,并使用 switchMap 到 return 不同的 Observable,一个包含新的(UPDATED_USER ) 行动。所以这只是一个转变。没有人必须调用效果。这是自动订阅的。

这样做的好处是,它使您的组件更简单,更易于测试。还将所有的副作用放在一个地方,这样更容易推理和测试。您的所有组件所做的就是订阅状态和调度操作。该效果将处理所有跑腿工作并分派适当的结果动作。

"Action Classes"而言,这些都不是强制性的。 Action 只不过是一个具有两个属性 typepayload 的对象,它们被传递给 reducer。采取行动 类 的目的是充当行动 创作者 。例如

class UserActions {
  static readonly USER_UPDATED = 'USER_UPDATE';

  userUpdated(user: User): Action {
    return {
      type: UserActions.USER_UPDATED,
      payload: user
    }
  }
}

现在您无需自己创建动作,只需调用适当的方法来获取动作

constructor(private userActions: UserActions) {}

@Effect
login$ = this.actions
  .ofType(LOGIN)
  .map(form => new Credentials(form))
  .switchMap(credentials => {
    return this.authService.login(credentials)
      .map(res => res.json() as User)
      .map(this.userActions.updateUser(user))
  });

您可以看到我们创建 Action 的最后一个 map 调用只是调用 userActions.updateUser

这样做有一些好处。其一,你会得到强类型。当您自己创建操作时,您可能会传递错误的负载。但是当你有一个你知道参数类型需要是什么的方法时,你就不太可能出错。

此外,当您使用动作创建器时,您可以接受不同的参数。它不一定是有效载荷。这样您就可以以可重现和可测试的方式自己创建有效载荷(并可能引入一些逻辑)。

另请参阅: