When I clicked on any item it is giving me an empty array in product detail page. The first time when I clicked one of the products it was giving me everything on this product id but after some time it showed me an empty array. and showing me this error ProductDetails.jsx:56 Uncaught TypeError: Cannot read properties of undefined (reading 'images'). I am using here reactjs 18 version, redux-toolkit, and node 16 version. Please help me solve this issue.
-----------------PeoductDetails.jsx ----------------------
import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux';
import MetaData from '../../more/MetaData'
import { getSingleProduct, STATUSES } from '../../store/slices/productSlice';
import Header from '../Home/Header'
import { useParams } from "react-router-dom";
import loading from "../../assets/loading.svg"
import Slider from "react-slick";
import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
import ShoppingBagOutlinedIcon from '@mui/icons-material/ShoppingBagOutlined';
const ProductDetails = () => {
const dispatch = useDispatch();
const { id } = useParams()
const { products, status } = useSelector((state) => state.product)
console.log(products);
const slider = {
dots: true,
infinite: true,
slidesToShow: 1,
slidesToScroll: 1,
autoplay: true,
speed: 3000,
autoplaySpeed: 6000,
cssEase: "linear"
};
useEffect(() => {
const fetchProduct = () => {
const action = getSingleProduct(id?.toString())
dispatch(action)
}
fetchProduct()
}, [dispatch, id])
console.log(products.product);
if (status === STATUSES.LOADING) {
return (
<div className="w-full h-[100vh] flex justify-center items-center bg-
gray-100">
<img src={loading} alt="" className="bg-transparent rounded-full h-
[300px]" />
</div>
);
}
return (
<>
<MetaData title="Product Details" />
<Header />
<div className="w-full flex justify-around md:flex-row flex-col">
<div>
<Slider {...slider}>
<div>
{products.product.images &&
products.product.images.map((item, i) => (
<img
key={i}
src={item.url}
alt={`${i} Slide`}
className="w-[350px] h-[350px] object-contain"
/>
))}
</div>
</Slider>
</div>
<div>
<h2>{products.product.name}</h2>
<div>
<span>({products.product.numOfReviews})</span>
</div>
<h2>₹{products.product.price}</h2>
<div>
<sapn>Quantity</sapn>
<div>
<button>-</button>
<input type="number" />
<button>+</button>
</div>
</div>
<p>{products.product.stock < 1 ? "OutOfStock" : "InStock"}</p>
<div>
{products.product.description}
</div>
<div>
<div>
<FavoriteBorderIcon />
<span>Add to wishlist</span>
</div>
<div>
<ShoppingBagOutlinedIcon />
<span>Add to Cart</span>
</div>
</div>
</div>
</div>
</>
)
}
export default ProductDetails
---------- productSlice.js -------------------------------------
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import * as api from "../api";
export const STATUSES = Object.freeze({
IDLE: 'idle',
ERROR: 'error',
LOADING: 'loading',
});
// Thunk
export const fetchAllProducts = createAsyncThunk("products/getAllProduct", async (_,
thunkAPI) => {
try {
const response = await api.getAllProducts()
return response.data
}catch(error){
return thunkAPI.rejectWithValue(error.response.data)
}
})
export const getSingleProduct = createAsyncThunk("product/getSingleProduct", async
(id, { rejectWithValue }) => {
try {
const response = await api.getSingleProduct(id)
console.log(response.data);
return await response.data
}catch(error){
console.log(error.response)
return rejectWithValue(error.response)
}
})
const productSlice = createSlice({
name: "product",
initialState: {
products: [],
status: STATUSES.IDLE,
message: ""
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(fetchAllProducts.pending, (state, action) => {
state.status = STATUSES.LOADING
})
.addCase(fetchAllProducts.fulfilled, (state, action) => {
state.products = action.payload
state.status = STATUSES.IDLE
})
.addCase(fetchAllProducts.rejected, (state, action) => {
state.status = STATUSES.ERROR
state.message = action.payload.message
})
.addCase(getSingleProduct.pending, (state, action) => {
state.status = STATUSES.LOADING
})
.addCase(getSingleProduct.fulfilled, (state, action) => {
state.products = action.payload
state.status = STATUSES.IDLE
})
.addCase(getSingleProduct.rejected, (state, action) => {
state.status = STATUSES.ERROR
state.message = action.payload.message
})
}
})
export default productSlice.reducer
----------------------- store.js ----------------------
import { configureStore } from "@reduxjs/toolkit";
import productSlice from "./slices/productSlice";
const store = configureStore({
reducer: {
product: productSlice
}
})
export default store
------- api.js ----------------
import axios from 'axios';
const API = axios.create({
baseURL: "http://localhost:5000",
headers: {
'Content-Type': 'application/json',
},
})
export const getAllProducts = async () => await API.get("/api/v2/products")
export const getSingleProduct = async (id) => await API.get(`/api/v2/products/${id}`)
------- Error ---
ProductDetails.jsx:56 Uncaught TypeError: Cannot read properties of undefined
(reading 'images')
at ProductDetails (ProductDetails.jsx:56:1)
at renderWithHooks (react-dom.development.js:16305:1)
at mountIndeterminateComponent (react-dom.development.js:20074:1)
at beginWork (react-dom.development.js:21587:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
at invokeGuardedCallback (react-dom.development.js:4277:1)
at beginWork$1 (react-dom.development.js:27451:1)
at performUnitOfWork (react-dom.development.js:26557:1)
at workLoopSync (react-dom.development.js:26466:1)
It seems like the products array is empty, you need a create a different state slice for single product, like currentProduct
. Remember empty array returns true
so products will return true and thus accessing any property on empty array will give errors. You can check if the array is empty by products.length > 0
or _.isEmpty()
from lodash
in ProductDetails.jsx
useEffect(() => {
const fetchProduct = () => {
//const action = getSingleProduct(id?.toString()) // remove
const action = getSingleProduct(id) // use
dispatch(action)
}
fetchProduct()
}, [dispatch, id])
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.