Hey,
Sorry to bring this issue up, because it's already too late to do anything about it, but I thought it still might be interesting for you.
I had the same problem with reducing reducers, but I made my own solution in form of composeReducers
, which works just like redux compose, but passes two arguments, one of which is modified along the chain (i.e. state).
I knew this is a very generic pattern, so I came up with this:
export function compose(...funcs) {
if (funcs.length === 0) {
return x => x;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (value, ...rest) => a(b(value, ...rest), ...rest));
}
Of course, in comparison with reduceReducers
, you can't pass an initialState, but you can pass an initialStateReducer
which initializes state for you. Also, execution direction is reversed - it goes from right to left (as in composition), instead of left to right (as in a chain or pipeline).
Since this pattern is very generic, you can compose not only reducers, but also string sanitizers, data parsers... hell, it's even compatible with redux store enhancers.
And some thoughts about initialState support: In my opinion, the best way to support initialState is:
reduceReducers(reducers = [], initialState = undefined)
This way, you're simplifying the function signature (think of using this in typescript), it is similar to Array.prototype.reduce(reducer, initialValue)
, and you give users the liberty to not use the initialState.
Right now, it forces you to write null
, which breaks the assumption that uninitialized state is undefined
, and the function signature is just weird.
So yeah, that's all I wanted to say, would be interesting to hear your thoughts.
EDIT: I've updated the implementation to use functional style, as in the current redux compose
implementation.