【问题标题】:Can I specify which reducer to dispatch to in ngrx我可以在ngrx中指定要分派到哪个reducer吗
【发布时间】:2017-05-22 15:35:05
【问题描述】:

我刚开始使用 redux,想知道是否有一种 clean 方式来使用多个 reducer。更具体地说,我想知道是否有一种方法可以指定我希望向哪个减速器发送操作

示例)假设我有两个减速器:userReducer、postReducer

用户缩减器

export function userReducer(state: User, action: Action) {
  switch (action.type) {]
    case SOME_ACTION:
      return state;
    default:
      return state || new User();
  }
}

后减速器

export function postReducer(state: Post, action: Action) {
  switch (action.type) {]
    case SOME_OTHER_ACTION:
      return state;
    default:
      return state || new Post();
  }
}

初始化
StoreModule.provideStore({user: userReducer, post: postReducer}),

我希望能够调用类似的东西

const store = this._store.select<User>('user');
store.dispatch({type: SOME_ACTION, payload: {blah: 'blah'}})

并且只调用 userReducer。

目前,当我发送一个动作时,userReducerpostReducer 都会更新它们的状态。这是 redux 工作的事实上的方式吗?

如果没有,有没有一种干净的方法来实现这一点?每次调度都会更新所有 reducer,这似乎很奇怪。

更新

我问这个问题是因为下面的部分。

default:
  return state || new User();

通过检查状态是否为空来返回默认值似乎不是一个好习惯。我希望能够做到以下几点。

default:
  return new User();

目前我不能这样做,因为如果我调度 SOME_ACTIONpostReducer.defaultuserReducer.SOME_ACTION 都将被调用。

不仅如此,我还可以想象如果我不小心为两个减速器创建了一个具有相同字符串的操作,因为它们都会被调用,那么调试将是一场噩梦。

【问题讨论】:

  • 能否请您具体说明一下为什么每次调度都会调用每个reducer?您是否有与这种行为有关的特定问题,或者您只是认为这很奇怪?我问,因为答案可能只是“redux 就是这样工作的”,或者是更深层次的误解。

标签: angular typescript redux ngrx


【解决方案1】:

简而言之不,您不能将操作分派给已注册减速器的子集。(至少不是以干净的方式,而且我认为在不更改 @ 的情况下不会ngrx/store-implementation 本身...)

这确实是 redux 的工作方式:每次调度一个动作时,store 调用所有具有当前状态 + 动作的 reducer 函数,并期望返回一个(新的)状态。如果reducer 不应该对一个动作做出反应,它必须返回它被调用的未改变状态(通过default 案例)。 (在我看来,这是 redux 的主要优势之一,因为它允许您对不同 reducer 中的相同操作做出反应)

为了实现你想要的行为,我建议你将状态初始化和默认情况分开:

export const initialState: User = new User();

export function userReducer(state: User = initialState, action: Action) {
    switch (action.type) {
        case SOME_ACTION:
            /* Work with the state and return a new state */
        default:
            return state;
    }
}

在 bootstrap 中,所有 reducer 函数都会以未定义状态和 init 动作调用。通过使用默认参数 (initialState) 将使用 new User() 初始化状态。如果您不想立即初始化状态,它还允许您设置initialState = undefined。 (我写了一个关于 store-initialisation here 的答案,如果你有兴趣的话)。

恐怕你不得不接受,那是你做不到的

default:
   return new User();

因为它会将状态的每个部分重置为默认状态 (new User()),正如您所注意到的。

不仅如此,我还可以想象这样一种场景,如果我不小心为两个 reducer 创建了一个具有相同字符串的操作,因为它们都会被调用,那么调试将是一场噩梦。

您是对的,您可能会不小心将两个操作命名为相同。 ngrx-example-app 中显示了缓解此问题的一种方法:

export const SEARCH =           '[Book] Search';
export const SEARCH_COMPLETE =  '[Book] Search Complete';

export class SearchAction implements Action {
    readonly type = SEARCH;

    constructor(public payload: string) { }
}

export class SearchCompleteAction implements Action {
    readonly type = SEARCH_COMPLETE;

    constructor(public payload: Book[]) { }
}

export type Actions
  = SearchAction
  | SearchCompleteAction

请注意,每个操作字符串都以实体名称为前缀。此外,这种方法允许在执行动作调度时对有效负载进行类型检查。 如果您真的对两个操作被命名为相同的行为感到偏执,您可以将它们命名为const MY_ACTION = '[Entity] MY_ACTION_565ef1b6-2c78-47b0-9b61-499ba9da3dde'(一个随机 GUID),这并不重要。 一个动作调度看起来还是一样的:

import * as book from '../actions/book';

this.store.dispatch(new book.SearchAction('hello'));

除非您正在开发一个拥有一个庞大团队的应用程序,否则我会说两个操作被命名为相同的可能性非常小。我还看到人们在应用程序初始化时检查重复的操作名称,尽管我现在找不到示例。

【讨论】:

    【解决方案2】:

    我可以在 ngrx 中指定要分派到哪个减速器

    不,这是设计使然。每个动作都被分派给每个 reducer,在你的 reducer 中,你只需决定是否要处理它。由于您在 reducer 中的状态应该是不可变的,因此如果您不处理特定操作,只需返回未触及的状态即可。

    你可以这样处理它:

    function myReducer(state = someInitialState, action) {
      case (action.type):
        'ACTION_1': 
          ...
        'ACTION_2': 
          ...
    
        default: 
          return state;
    }
    

    这样,当你的状态未定义时,它会被设置为初始值someInitialState

    如果你不处理一个动作,就会返回相同的状态。

    也就是说,这是基本的Redux,我强烈建议您在深入了解ngrx 之前先看看http://redux.js.org

    【讨论】:

      猜你喜欢
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 2017-12-27
      • 1970-01-01
      • 2011-11-30
      • 2021-09-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多