I'm trying to wrap my head around how to change deeply-nested state in redux. It makes sense to me to combine reducers and change first-level properties of the state for those pieces. Where I'm not so clear is on how to change the state of a deeply-nested property. Let's pretend that I have a shopping cart app. Below is my state:
{
cart: {
items: []
},
account: {
amountLeft: 100,
discounts: {
redeemed: [],
coupons: {
buyOneGetOne: false
}
}
}
}
When a user enters in a code, let's say they can redeem the "buyOneGetOne" coupon and that value should become true. I have a reducer for cart and another for account. For a first-level property (like if I was clearing the cart's items), I would just do the following in my reducer:
case 'EMPTY_CART':
return Object.assign({}, state, {items: []});
For changing buyOneGetOne, however, it seems like I would first need to do an Object.assign on coupons (because buyOneGetOne was modified), then doing an Object.assign on discounts (because I had modified coupons), and then finally emitting the action so the reducer could do Object.assign on the account (because discounts has now changed). This seems really complicated and easy to do wrong, though, which leads me to believe there must be a better way.
Am I going about this all wrong? It seems like reducers are only used to modify the root level properties of the state (like cart and account) and that I shouldn't have a reducer that touches state inside the account (like a discounts reducer), because account already has a reducer. But when I only want to change one property far down the state tree, it gets complex to merge every object from that change all the way up the object chain to the child of the root...
Can you/should you have reducers inside of reducers, like in this case have a discounts reducer?
Reducers and Immutable Updates One of the primary rules of Redux is that our reducers are never allowed to mutate the original / current state values!
The only way to update a state inside a store is to dispatch an action and define a reducer function to perform tasks based on the given actions. Once dispatched, the action goes inside the reducer functions which performs the tasks and return the updated state to the store. This is what Redux is all about.
Redux: Update an Object When you want to update the top-level properties in the Redux state object, copy the existing state with ... state and then list out the properties you want to change, with their new values.
How do I share state between two reducers? Do I have to use combineReducers ? The suggested structure for a Redux store is to split the state object into multiple “slices” or “domains” by key, and provide a separate reducer function to manage each individual data slice.
You can definitely have reducers inside of reducers; in fact, the redux demo app does this: http://redux.js.org/docs/basics/Reducers.html.
For example, you might make a nested reducer called `discounts':
function discounts(state, action) {
switch (action.type) {
case ADD_DISCOUNT:
return state.concat([action.payload]);
// etc etc
}
}
And then make use of this reducer in your account reducer:
function account(state, action) {
switch (action.type) {
case ADD_DISCOUNT:
return {
...state,
discounts: discounts(state.discounts, action)
};
// etc etc
}
}
To learn more, check out the egghead.io redux series by Dan himself, specifically the reducer composition video!
you should nest combinedReducers
for each level deep
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