I need to update multiple properties on my application state with single reducer. Later I'm using combineReducers
redux helper that combines all the reducers. Problem is, all the other reducers operate one level down from the root of the state (that is, on a property), except this one that I need to pass the whole state to.
How do I use the combineReducers
function to pass the root state?
const App = combineReducers({
pages: combineReducers({
browse: combineReducers({
numOfItems: loadMoreItems,
filter: setFilter
})
}),
<rootState>: openPage,
favoriteItems: addItemToFavorites,
inBasketItems: addItemToBasket
})
What do I place in <rootState>
?
There are two main ways to initialize state for your application. The createStore method can accept an optional preloadedState value as its second argument. Reducers can also specify an initial value by looking for an incoming state argument that is undefined , and returning the value they'd like to use as a default.
You can set it at the reducers . Reducers can also set initialState by looking at the incoming state argument (which would be undefined if createStore is not called with initialState ) and returning the values they would like to use as default.
The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore .
Redux uses a single root reducer function that accepts the current state (and an action) as input and returns a new state.
The Redux docs cover this topic in the section Structuring Reducers - Beyond combineReducers
. Basically, you need to use more than just combineReducers
to accomplish what you want. I also give an example of writing some custom reducer structuring logic in my recent blog post Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers.
After some refactoring I found that best is not to use combineReducers
in this situation at all. Instead I structured my root reducer like so:
const App = (state = initialState, action) => {
state = Object.assign({}, state)
switch (action.type) {
case OPEN_PAGE:
state = openPage(state, action)
case LOAD_MORE_ITEMS:
state.pages.browse.numOfItems = loadMoreItems(state.pages.browse.numOfItems, action)
case SET_FILTER:
state.pages.browse.filter = setFilter(state.pages.browse.filter, action)
case ADD_ITEM_TO_FAVORITES:
state.favoriteItems = addItemToFavorites(state.favoriteItems, action)
case ADD_ITEM_TO_BASKET:
state.inBasketItems = addItemToBasket(state.inBasketItems, action)
}
return state
}
Maybe it would make sense to use combineReducers
if any of reducers that work on particular part of state were more complicated, but it worked like this for me. I still kept some boilerplate in sub-reducers for readability purpose, like so:
const loadMoreItems = (state = initialState.pages.browse.numOfItems, action) => {
switch (action.type) {
case LOAD_MORE_ITEMS:
return state = action.number
default:
return state
}
}
So basically I keep the state initialization and action name since it makes it easier to understand what this reducer is all about.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With