I have a reducer that use for search and realized that it needs to be used for multiple un-related search components. So, looking through the Redux documentation I found the concept of higher order reducers (http://redux.js.org/docs/recipes/reducers/ReusingReducerLogic.html#customizing-behavior-with-higher-order-reducers) (meta reducers in ngrx) and used that to create 2 'instances' of my search reducer. I then found in the same documentation that this will appear to work with selectors but actually has an issue with the memoization (http://redux.js.org/docs/recipes/ComputingDerivedData.html#accessing-react-props-in-selectors). That article references a function called 'mapStateToProps' which seems to be React specific way of connecting the store data to components (if I understand it correctly...).
Is there an equivalent in ngrx or is there another way of creating these selectors to work with the different instances of the reducers?
Below is a mildly contrived example based on the ngrx example app of what I am trying to accomplish:
reducers/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) {
//...
}
}
}
reducers/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);
I believe the getSearchIds selector above needs the ability somehow to specify which instance of the search Reducer it is accessing. (Strangely, in my code it seems to work but I am not sure how it knows which to select from and I assume it has the memoization issue discussed in the Redux documentation).
While Kevin's answer makes sense for the contrived code example I gave, there are definitely maintenance issues if each reducer 'instance' has many properties or if you need many 'instances'. In those cases you would wind up with many quasi-duplicate properties on a single reducer (ex. 'bookIds', 'magazineIds', 'dvdIds', 'microficheIds', etc.).
With that in mind, I went back to the Redux documentation and followed it to the FAQ for selectors, specifically How Do I create a Selector That Takes an Argument.
From that information, I put this together:
reducers/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);
}
In some component where you know the reducer you want to use:
//...
class SearchComponent {
@Input()
searchType: string = 'books';
ids: Observable<number>;
constructor(private store: Store<fromRoot.State>) {
this.store.select(fromRoot.getSearchIds(searchType));
}
}