2

我有一个用于搜索的减速器,并意识到它需要用于多个不相关的搜索组件。因此,通过查看 Redux 文档,我发现了高阶 reducer 的概念(http://redux.js.org/docs/recipes/reducers/ReusingReducerLogic.html#customizing-behavior-with-higher-order-reducers)(meta ngrx 中的减速器)并用它来创建我的搜索减速器的 2 个“实例”。然后我在同一个文档中发现这似乎可以与选择器一起使用,但实际上在记忆化方面存在问题(http://redux.js.org/docs/recipes/ComputingDerivedData.html#accessing-react-props-in-选择器)。那篇文章引用了一个名为“mapStateToProps”的函数,它似乎是将存储数据连接到组件的 React 特定方式(如果我理解正确的话......)。

在 ngrx 中是否有等价物,或者是否有另一种方法可以创建这些选择器来处理不同的 reducer 实例?

下面是一个基于我想要完成的 ngrx 示例应用程序的稍微做作的示例:

减速器/searchReducer.ts:

export interface State {
  ids: string[];
  loading: boolean;
  query: string;
};

const initialState: State = {
  ids: [],
  loading: false,
  query: ''
};

export const createSearchReducer = (instanceName: string) => {
  return (state = initialState, action: actions.Actions): State => {
    const {name} = action; // Use this name to differentiate instances when dispatching an action.
    if(name !== instanceName) return state;

    switch (action.type) { 
      //...
    }
  }
}

减速器/index.ts:

export interface State {
  search: fromSearch.State;
}

const reducers = {
  search: combineReducers({
    books: searchReducer.createReducer('books'),
    magazines: searchReducer.createReducer('magazines')
  }),
}


export const getSearchState = (state: State) => state.search;

// (1)
export const getSearchIds = createSelector(getSearchState, fromSearch.getIds);

我相信上面的 getSearchIds 选择器需要能够以某种方式指定它正在访问的搜索 Reducer 的哪个实例。(奇怪的是,在我的代码中它似乎可以工作,但我不确定它是如何知道从哪个中选择的,我认为它存在 Redux 文档中讨论的记忆问题)。

4

2 回答 2

2

虽然 Kevin 的回答对于我给出的人为代码示例是有意义的,但如果每个减速器“实例”具有许多属性或者您需要许多“实例”,则肯定存在维护问题。在这些情况下,您最终会在单个减速器上获得许多准重复的属性(例如“bookIds”、“magazineIds”、“dvdIds”、“microficheIds”等)。

考虑到这一点,我回到了 Redux 文档,并按照它查看了选择器的常见问题解答,特别是How Do I create a Selector That Takes an Argument

根据这些信息,我将其放在一起:

减速器/index.ts:

export const getBookSearchState = (state: State) => state.search;
export const getMagazineSearchState = (state: State) => state.search;

// A function to allow the developer to choose the instance of search reducer to target in their selector. 
export const chooseSearchInstance = (instance: string): ((state: State) => searchReducer.State) => {
    switch(instance) {
        case 'books': {
            return getBookSearchState;
        }
        case 'magazines': {
            return getMagazineSearchState;
        }
    }
}

// Determines the instance based on the param and returns the selector function.
export const getSearchIds = (instance: string) => {
    const searchState = chooseSearchInstance(instance);
    return createSelector(searchState, state => state.ids);
}

在您知道要使用的减速器的某些组件中:

//...
class SearchComponent {
    @Input()
    searchType: string = 'books'; 
    ids: Observable<number>;

    constructor(private store: Store<fromRoot.State>) {    
        this.store.select(fromRoot.getSearchIds(searchType));
    }
}
于 2017-07-20T23:43:17.313 回答
0

I would recommend rethinking your way of doing this and use the same reducer and make another switch case.

Unrelated to that, the newer version of AOT doesn't like using '=>' to create your reducers. Instead use

export function SearchReducer (state : State = initialState, { type, payload }){
   switch (type) {
      //cases...
   }
}

and you won't have to use combineReducers, you can just build out your reducer object

let reducers = {
   search: SearchReducer
}

Saying your state is of the interface State type lets you take advantage of that typing.

于 2017-07-14T19:28:58.160 回答