电商项目获取单商品数据时的报错问题求助
问题:电商单商品页面数据加载报错
开发电商项目时,通过API根据ID获取单条商品数据的功能,在以下场景出现报错:
- 刷新页面
- 快速跳转至该页面
- 在单商品页面点击更新Redux状态的按钮
报错截图:
已尝试设置初始对象避免报错,但未生效,恳请帮忙解决。
相关代码
singleListingSlice.js
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit" import { toast } from "react-toastify" import customFetch from "../../utils/axios" const initialState = { isLoading: false, isError: false, createSuccess: false, listing: {}, } export const getSingleListing = createAsyncThunk( "singleListing/getSingleListing", async (id, thunkAPI) => { try { const { data } = await customFetch(`/listing/${id}`) return data.listing } catch (error) { return thunkAPI.rejectWithValue(error.response.data.msg) } } ) const singleListingSlice = createSlice({ name: "singleListing", initialState, reducers: {}, extraReducers: (builder) => { builder .addCase(getSingleListing.pending, (state) => { state.isLoading = true }) .addCase(getSingleListing.fulfilled, (state, { payload }) => { state.isLoading = false state.listing = payload toast.success(`done fetching`, { toastId: "success", }) }) .addCase(getSingleListing.rejected, (state) => { state.isLoading = false state.isError = true toast.error(`can't find listing, please try again`) }) }, }) export default singleListingSlice.reducer
singleListingPage.jsx
import { useState, useEffect } from "react" import { useSelector, useDispatch } from "react-redux" import { useParams } from "react-router-dom" import { Searchbox, UserInfo, ReviewCard, OfferBox, Slider, } from "../components" import { getSingleProduct } from "../features/singleProduct/singleProductSlice" const initialState = { condition: "", createdAt: "", createdBy: "", dealMethod: { meetUp: true, delivery: false, }, description: "", meetUpLocation: "", name: "", photos: [], previousPrice: "", price: "", soldTo: "", status: "", updatedAt: "", _id: "", } const SingleListingPage = () => { const [listing, setListing] = useState(initialState) const { user } = useSelector((store) => store.user) const { listing: storeListing, isLoading, isError, } = useSelector((store) => store.singleListing) const { id: listingId } = useParams() const dispatch = useDispatch() useEffect(() => { dispatch(getSingleListing(listingId)) }, [listingId]) useEffect(() => { setListing(storeListing) }, [storeListing]) if (isLoading) { return <h1>Loading...</h1> } if (isError) { return <h1>Error</h1> } const { condition, createdAt, createdBy, dealMethod, description, meetUpLocation, name, numOfFavorite, photos, previousPrice, price, soldTo, status, updatedAt, _id, } = listing let tradeMethod = [] Object.entries(dealMethod).forEach((method) => { if (!method[1]) return tradeMethod = [...tradeMethod, method[0]] }) return ( <Wrapper> <div className="container"> <Searchbox /> <div className="bread-crumb">category / {name}</div> <SingleListingSlider photos={photos} /> <section className="listing-information"> <div className="details"> <div className="row"> <h3 className="name">{name}</h3> <div className="favorite"> {numOfFavorite > 0 ? <FaRegHeart /> : <FaHeart />} <span>{numOfFavorite}</span> </div> </div> <h2 className="price">HK${price}</h2> <span>condition: {condition}</span> {/* <span>trade method: {tradeMethod}</span> */} <span>location: {meetUpLocation}</span> </div> <OfferBox /> </section> <section className="section-description"> <h2 className="title">Description</h2> <article>{description}</article> </section> <section className="section-review"> <div className="col"> <UserInfo {...user} /> </div> <div className="col"> <h2 className="title">reviews for @{name}</h2> <div className="reviews"> <ReviewCard /> <ReviewCard /> <ReviewCard /> </div> </div> </section> </div> </Wrapper> ) } export default SingleListingPage
问题分析与解决方案
核心问题
- Redux初始状态的
listing是空对象,且缺少numOfFavorite等组件需要的字段,数据加载前解构空对象会触发报错。 - 组件维护了本地
listing状态,和Redux状态同步时,会被空的storeListing覆盖,丢失初始默认值。 - 快速操作时,
isLoading状态可能未及时拦截渲染,导致提前访问未加载的属性。
修复步骤
1. 同步Redux初始状态字段
把组件里的完整初始对象同步到Redux的initialState中,确保所有需要解构的字段都有默认值:
// singleListingSlice.js const initialState = { isLoading: false, isError: false, createSuccess: false, listing: { condition: "", createdAt: "", createdBy: "", dealMethod: { meetUp: true, delivery: false, }, description: "", meetUpLocation: "", name: "", numOfFavorite: 0, // 补上缺失字段 photos: [], previousPrice: "", price: "", soldTo: "", status: "", updatedAt: "", _id: "", }, }
2. 移除本地状态,直接使用Redux状态
不需要维护本地listing,直接用Redux的storeListing,避免状态同步冲突:
// singleListingPage.jsx // 删除本地状态相关代码: // const initialState = {...} // const [listing, setListing] = useState(initialState) // useEffect(() => { setListing(storeListing) }, [storeListing]) // 直接解构Redux状态 const { condition, createdAt, ... // 其他字段 numOfFavorite, } = storeListing
3. 可选:添加兜底校验
在JSX中使用可选链操作符,避免极端情况的空值报错:
{numOfFavorite?.toString() || '0'} <SingleListingSlider photos={photos || []} />
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

