I currently am creating a website with react-redux and was getting an error with a typo in the preloadedState variable within createStore. My relevant store code is as follows (Note the cartitems
spelling in initialState
instead of cartItems
):
const reducer = combineReducers({
productList: productListReducer,
productDetails: productDetailsReducer,
cart: cartReducer,
})
const cartItemsFromStorage = localStorage.getItem('cartItems') ?
JSON.parse(localStorage.getItem('cartItems')) : []
//BELOW GETS PASSED INTO createStore
const initialState = {
cart: {cartitems: cartItemsFromStorage}
}
Within my cartReducer
the code being affected is as follows:
export const cartReducer = (state = {cartItems: []}, action) => {
switch(action.type){
case CART_ADD_ITEM:
const item = action.payload
const existItem = state.cartItems.find(x => x.product === item.product)
***other code below...***
default:
return state
}
I noticed that this throws an Unhandled Rejection (TypeError): state.cartItems is undefined
error on the existItem
line. Why does the addition of the preloadedState cause this issue? From my understanding the reducer's default state (in this case given by state = {cartItems: []}
) should still be accessible from within the reducer? Is this not the case?
This is by design. From the doc Initializing State:
Without
combineReducers()
or similar manual code,preloadedState
always wins overstate = ...
in the reducer because the state passed to the reducer ispreloadedState
and is notundefined
, so the ES6 argument syntax doesn't apply.
With
combineReducers()
the behavior is more nuanced. Those reducers whose state is specified inpreloadedState
will receive that state. Other reducers will receiveundefined
and because of that will fall back to thestate = ...
default argument they specify.
For your case, the preloadedState
is {cart: { cartitems: cartItemsFromStorage }}
, the { cartitems: cartItemsFromStorage }
object will be passed in cartReducer
as it's default state rather than the ES6 default argument syntax.
That's why your cart state shape is {cartitems: cartItemsFromStorage}
If the preloadedState
is undefined
, then your cart default state is {cartItems: []}
.