Usually when someone creates a redux app, the root reducer gets configured within
Suppose we have a simple state, that with fields
location and also it should track failures related to fetch of user or location. Also location failures must be displayed as warnings, and user related as errors. Those errors should be displayed as stack:
We could add a field called
errors to both
location sub-states, and our state would look like:
- Any domain of the application can handle error tracking separately.
- In case of successful fetch of location or user, corresponding errors might be easily removed from state.
- Hard to select all errors in the app, and display those in the error stack. Hypothetic
selectErrorsfunction should be aware of all modules in the app, that might track errors.
- Modules should reason not only about logging errors, but also about removing those with cross button.
Another potential approach would be in creating separate sub-state for errors:
- Easy to select all active errors.
- You can write separate reducer, that will handle removing of any error with cross button.
errorsreducer should handle failure actions for all modules in the app.
- In case of successful fetch of location or user (and any mid-size app will have at least 5–10 modules), corresponding errors might be removed within logic inside
The main issue of both approaches occurs due to assumption, that any reducer should manage only its own part of the state. This is paradigm, brought to us by
combineReducers , which will delegate all top level state values to individual reducers. Hence there’s no way to modify anything outside of area of responsibility of certain reducer.
As an alternative, full state might be passed and returned by each individual reducer:
You might notice, that in
rootReducer state is explicitly piped through each reducer. This means that
userReducer will manage previous store state, then result of
userReducer will be passed as state parameter to the
locationReducer and so on. This actually means that if reducers would be stored in an array, there could be a universal function that would organize that pipeline. This function is called reduceReducers and it can be imported from `reduce-reducers` npm package. Reducer code can now be simplified:
- You can easily select all errors, and manage them in the
errorsReducer. However you can still add
locationrelated errors in appropriate reducers.
- You can post-process your state. For example if you never want to store more than 7 errors, you can just create a
errorsCleanUpReducerand add it as last reducer in the list.
- Every reducer is now aware of full state, it gets harder to perform update operations.
reduceReducers is not a replacement of
combineReducers , in fact you may try use them together. Let extract
locationReducer , those will just manage errors related to their modules. Now we can reduce all errors related reducers, and combine them together with
Now each module can track its own errors, however those errors are stored in one source of truth. To get more familiar with this approach play with example above on this codesandbox.