Error type: Store does not have a valid reducer. Make sure the argument passed to combineReducers is an object whose values are reducers.
I am trying to add a Store and my app is failing. I don't understand where the error is coming from. Can someone point out my error and how to fix it?
store
import { configureStore } from '@reduxjs/toolkit';
import rootReducers from './reducers';
const store = configureStore({
reducer: rootReducers,
});
export default store;
reducer
import handleCart from './handleCart';
import { combineReducers } from 'redux';
const rootReducers = combineReducers({
handleCart,
});
export default rootReducers;
const cart = [];
const handleCart = (state = cart, action) => {
const product = action.payload;
switch (action.type) {
case ADDITEM:
// Check product exist
const exist = state.find(x => x.id === product.id);
if (exist) {
return state.map(x =>
x.id === product.id ? {...x, qty: x.qty + 1} : x,
);
} else {
const product = action.payload;
return [
...state,
{
...product,
qty: 1,
},
];
}
break;
case REMOVEITEM:
const exist1 = state.find(x => x.id === product.id);
if (exist1.qty === 1) {
return state.filter(x => x.id !== exist1.id);
} else {
return state.map(x =>
x.id === product.id ? {...x, qty: x.qty - 1} : x,
);
}
break;
default:
break;
}
};
I see only a couple overt issues in the handleCart
reducer function code, either could be cause for throwing an exception. It seems the handleCart
reducer function is missing returning state in the default case. The REMOVEITEM
case also incorrectly assumes exist
is defined and accesses a qty
property of a potentially undefined object. Remember that Array.prototype.find
returns undefined
when no matching element is found in the array.
Make sure the handleCart
is default exported so it can be imported to create the root reducer.
const cart = [];
const handleCart = (state = cart, action) => {
const product = action.payload;
switch (action.type) {
case ADDITEM: {
// Check product exist
const exist = state.find(x => x.id === product.id);
if (exist) {
return state.map(x =>
x.id === product.id ? { ...x, qty: x.qty + 1 } : x,
);
}
return [...state, { ...product, qty: 1 }];
}
case REMOVEITEM: {
const exist = state.find(x => x.id === product.id);
if (exist) {
// item exists, update quantity
if (exist.qty === 1) {
return state.filter(x => x.id !== exist1.id);
} else {
return state.map(x =>
x.id === product.id ? { ...x, qty: x.qty - 1 } : x,
);
}
}
// item didn't exist, just return current state
return state;
}
default:
// No action to do, return current state
return state;
}
};
export default handleCart;
You are using redux-toolkit, why are you not creating state slice? Here's a similar implementation using createSlice
. This allows you to write the reducer functions using mutable updates instead of needing to shallow copy all the parts of the state that are being updated.
import { createSlice } from '@reduxjs/toolkit';
const initialState = [];
const cartSlice = createSlice({
name: "cart",
initialState,
reducers: {
addItem: (state, action) => {
const product = state.find(product => product.id === action.payload.id);
if (product) {
product.qty++;
} else {
state.push({ ...action.payload, qty: 1 });
}
},
removeItem: (state, action) => {
const product = state.find(product => product.id === action.payload.id);
if (product) {
product.qty--;
if (product.qty === 0) {
state.filter(product => product.id !== action.payload.id);
}
}
},
},
});
export const { addItem, removeItem } = cartSlice.actions;
export default cartSlice.reducer;