I am in the process of creating an ecommerce website where I stumble on this problem when I try to run the addToCartHandler through a button. I have everything as I am supposed to but for some unknown reason, the cartItems I try to push data to returns undefined with the following error message TypeError: Cannot read properties of undefined (reading 'push')
, which typically indicates that somewhere in your code, an attempt is made to access the push method which is state.cartItems array that is undefined.
Please help me find a possible solution with the related code below:
store/index.js
import { configureStore } from "@reduxjs/toolkit";
import { combineReducers } from "redux";
import thunk from "redux-thunk";
import storage from "redux-persist/lib/storage";
import { persistReducer } from "redux-persist";
import cart from "./cartSlice";
const reducers = combineReducers({ cart });
const config = {
key: "root",
storage,
};
const reducer = persistReducer(config, reducers);
const store = configureStore({
reducer: reducer,
devTools: process.env.NODE_ENV !== "production",
middleware: [thunk],
});
export default store;
store/index.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
cartItems: [],
};
export const cartSlice = createSlice({
name: "cart",
initialState,
reducers: {
addToCart(state, action) {
console.log("Adding to cart:", action.payload);
state.cartItems.push(action.payload);
},
updateCart(state, action) {
state.cartItems = action.payload;
},
emptyCart(state, action) {
state.cartItems = [];
},
},
});
export const { addToCart, updateCart, emptyCart } = cartSlice.actions;
export default cartSlice.reducer;
pages/_app.js
import "@/styles/globals.scss";
import { Provider } from "react-redux";
import { SessionProvider } from "next-auth/react";
import { PersistGate } from "redux-persist/integration/react";
import { persistStore } from "redux-persist";
import Head from "next/head";
import store from "@/store";
let persistor = persistStore(store);
export default function App({
Component,
pageProps: { session, ...pageProps },
}) {
return (
<>
<Head>
<title>Nails Republic</title>
<meta
name="description"
content="The one stop online shop for all of your manicure and pedicure needs."
/>
<link rel="icon" href="/nails_republic_icon.png" />
</Head>
<SessionProvider session={session}>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Component {...pageProps} />
</PersistGate>
</Provider>
</SessionProvider>
</>
);
}
productPage
import Rating from "@mui/material/Rating";
import React, { useState, useEffect } from "react";
import { useRouter } from "next/router";
import Link from "next/link";
import { TbPlus, TbMinus } from "react-icons/tb";
import { BsHandbagFill, BsHeart } from "react-icons/bs";
import { useDispatch, useSelector } from "react-redux";
import { signIn, useSession } from "next-auth/react";
import axios from "axios";
import styles from "./styles.module.scss";
import { addToCart, updateCart } from "@/store/cartSlice";
export default function Infos({ product, setActiveImg }) {
const router = useRouter();
const dispatch = useDispatch();
const { data: session } = useSession();
const [size, setSize] = useState(router.query.size);
const [qty, setQty] = useState(1);
const [error, setError] = useState("");
const [success, setSuccess] = useState("");
const { cart } = useSelector((state) => ({ ...state }));
console.log("Cart state:", cart);
useEffect(() => {
setSize("");
setQty(1);
}, [router.query.style]);
useEffect(() => {
if (qty > product.quantity) {
setQty(product.quantity);
}
}, [router.query.size]);
const addToCartHandler = async () => {
if (!router.query.size) {
setError("Please Select a size");
return;
}
const { data } = await axios.get(
`/api/product/${product._id}?style=${product.style}&size=${router.query.size}`
);
if (qty > data.quantity) {
setError(
"The selected quantity is more than in stock. Try and lower the Qty"
);
} else if (data.quantity < 1) {
setError("This Product is out of stock.");
return;
} else {
let _uid = `${data._id}_${product.style}_${router.query.size}`;
dispatch(
addToCart({
...data,
qty,
size: data.size,
_uid,
);
}
};
return (
<div className={styles.infos}>
<div className={styles.infos__container}>
<h1 className={styles.infos__name}>{product.name}</h1>
<h2 className={styles.infos__sku}>{product.sku}</h2>
<div className={styles.infos__rating}>
<Rating
name="half-rating-read"
defaultValue={product.rating}
precision={0.5}
readOnly
style={{ color: "#5a141d" }}
/>
({product.numReviews}
{product.numReviews == 1 ? " review" : " reviews"})
</div>
<div className={styles.infos__price}>
{!size ? <h2>{product.priceRange}</h2> : <h1>{product.price}₦</h1>}
{product.discount > 0 ? (
<h3>
{size && <span>{product.priceBefore}₦</span>}{" "}
<span>(-{product.discount}%)</span>
</h3>
) : (
""
)}
</div>
<span className={styles.infos__shipping}>
{product.shipping
? `+${product.shipping}$ Shipping fee`
: "Free Shipping"}
</span>
<span>
{size
? product.quantity
: product.sizes.reduce((start, next) => start + next.qty, 0)}{" "}
pcs left.
</span>
<div className={styles.infos__sizes}>
<h4>Select a Size : </h4>
<div className={styles.infos__sizes_wrap}>
{product.sizes.map((size, i) => (
<Link
href={`/product/${product.slug}?style=${router.query.style}&size=${i}`}
>
<div
className={`${styles.infos__sizes_size} ${
i == router.query.size && styles.active_size
}`}
onClick={() => setSize(size.size)}
>
{size.size}
</div>
</Link>
))}
</div>
</div>
<div className={styles.infos__colors}>
{product.colors &&
product.colors.map((color, i) => (
<span
className={i == router.query.style ? styles.active_color : ""}
onMouseOver={() =>
setActiveImg(product.subProducts[i].images[0].url)
}
onMouseLeave={() => setActiveImg("")}
>
<Link href={`/product/${product.slug}?style=${i}`}>
<img src={color.image} alt="" />
</Link>
</span>
))}
</div>
<div className={styles.infos__qty}>
<button onClick={() => qty > 1 && setQty((prev) => prev - 1)}>
<TbMinus />
</button>
<span>{qty}</span>
<button
onClick={() => qty < product.quantity && setQty((prev) => prev + 1)}
>
<TbPlus />
</button>
</div>
<div className={styles.infos__actions}>
<button
disabled={product.quantity < 1}
style={{ cursor: `${product.quantity < 1 ? "not-allowed" : ""}` }}
onClick={() => addToCartHandler()}
>
<BsHandbagFill />
<b>ADD TO CART</b>
</button>
</div>
{error && <span className={styles.error}>{error}</span>}
{success && <span className={styles.success}>{success}</span>}
</div>
</div>
);
}