
When an application using
Redux grows to a sufficiently large size, the number of states increases many times. For the separation of reducers into logical units, an approach of combining them using
combineReducers is used . This solution allows you to expand the
store vertically. But there are times when this separation may not be enough. For example, one of the levels carries a composite logic, which it would also be nice to divide (or as one of the famous people said: “Enhance!”). But this approach is not in the Redux API. And the search for a solution to this issue did not give anything either (maybe I looked badly). Therefore, I developed my approach to expanding along the “horizontal” Redux Store.
I want to introduce you to
my project , which allows you to implement this approach.
Using
1) First, at the level 1 level reducer, we include the library itself:
')
import {stateCombine, runCombine, getInitialState} from "redux-combine-deep-props";
2) Connect the reducer for the second level:
import level2Module from "./reducer-level-2";
3) Form the initial values for the first level:
let initialState = { propLevel1: ..., ... propLevelN: ... };
4) Create a combination object:
let combinations = { <name prop>: { module: level2Module } };
where we set the name of the future section of the
name prop , and for it - a reducer of this level,
as well as a set of types of eshenov for triggering changes to the state of this level .
5) Create a handler function for the current state:
let combineDeepProp = stateCombine(combinations); let combine = runCombine(combinations, combineDeepProp);
6) To handle the initial values of all levels, create a combined initial state.
const combineInitialState = getInitialState(combinations, initialState);
7) In the export-reducer function, we use the combined
initial state , and in its body,
strictly before any state change, we start the handler of all combinations, which changes the current state as necessary, if the current action type matches the specified ones:
export default function level1Module (state = combineInitialState, action) { ... let newState = combine(state, action); ... switch (action.type) { case "....": newState = { ...newState, ... }; break; ... }; ... return newState; };
8) The module of the second level is made out according to the standard scheme, taking into account that the state in it is presented in the context of this level:
let initialState = { ... }; export default function search(state = initialState, action) { ... switch (action && action.type) { ... }; };
but with one difference - there should be a check for
undefined current
action . Made for setting the
initial state on the first pass in the
getInitialState method.
Conclusion
This approach allows in the recursive mode to expand to infinity the current level and "vertical", due to the use in combinations of more than one object:
let combinations = { <name prop1>: { module: level2Module1 }, ... <name propN>: { module: level2ModuleN } };
and horizontally, by using the approach described above on each of the 2+ levels.
→
SourcesUPD:Full code refactoring, thank you very much
dagen , for pointing out the problem of mutability. Now the principle of use has changed a little, see p.7 and p.4 - a set of actions is now absent as unnecessary, but left the combination as an object for possible further expansion of the functional. I note that I used this approach in
conjunction with
PolymerJS , and then with
VueJS , and used
polymer-redux and
vuedeux libraries for integration with
Redux, respectively. And since there is a bandage on specific properties of the state, they follow the path of mutations, since it was not necessary to mutate the root state when one of the subtrees was mutated.
UPD2:Added
rollup collector to compile project