Problem
I'd like to chain multiple high order reducers using ngrx, so that similar parts of code in my reducers have only one implementation.
My application has number of pages that have quite similar functionalities. Their reducers also look quite similar. For this example let's consider an application with three pages: Page One, Page Two and Page Three. Each of these pages contains a counter, but is allowed to do different things with it. And so:
- Page One can increment and decrement value of counter
- Page Two can increment, decrement and reset value of counter
- Page Three can increment and reset value of counter
Example of such application can be found here. It is very naive implementation with separate reducer for each page - a lot of very similar functionalities are repeated in each reducer.
Solution with only one high order reducer
I managed to move part of common logic to separate piece of code using high order reducer. In this case it is increment functionality:
interface Actions {
incrementAction: ActionCreator<string>;
}
export const withIncrementation = ({ incrementAction }: Actions) => (
initState,
...actions
) =>
createReducer(
initState,
on(incrementAction, state => ({
...state,
counter: state.counter + 1
})),
...actions
);
This high order reducer can be used like this:
const pageOneReducer = withIncrementation({
incrementAction: PageOneActions.IncrementRequested
})(
initialState,
on(PageOneActions.DecrementRequested, (state: State) => ({
...state,
counter: state.counter - 1
}))
);
export function reducer(state: State | undefined, action: Action) {
return pageOneReducer(state, action);
}
Up to this point everything works fine. Application that works correctly like this is here
Problem with making multiple high order reducers work
Problem starts, when I try to chain multiple high order reducers to work. In example application both Page Two and Page Three are capable of resetting counter (and incrementing too), so I'd like to use 2 high order reducers now. I prepared new high order reducer, very similar to the previous one, that should do the work:
export const withReseting = ({ resetAction }: Actions) => (
initState,
...actions
) =>
createReducer(
initState,
on(resetAction, state => ({
...state,
counter: initialState.counter
})),
...actions
);
It works fine when it's alone, however I can't find a way to make it work in such way, that both withIncrementation and withReseting work fine on the same reducer. Example application where I tried to achieve that can be found here, but it does not work (it seems like my state stops working at all).
Naive approach with 3 separate reducers