MERN电商项目:商品详情页刷新后空白且商品消失求助
MERN栈电商项目ProductDetails页面问题求助
我是MERN栈初学者,跟着教程搭建电商网站,目前在前端ProductDetails页面遇到两个问题:
- 点击商品可正常进入详情页,但刷新页面后变为空白
- 返回首页后,该商品会消失
以下是相关代码及报错信息:
ProductDetails.js代码
import Carousel from "react-material-ui-carousel"; import "./ProductDetails.css"; import { useSelector, useDispatch } from "react-redux"; import { getProductDetails } from '../../actions/productAction'; import { useEffect } from 'react'; const ProductDetails = ({match}) => { const dispatch = useDispatch(); const { product, loading, error} = useSelector( (state) => state.productDetails); useEffect( () => { dispatch(getProductDetails(match.params.id)); }, [dispatch, match.params.id]); return ( <Fragment> <div className="ProductDetails"> <div> <Carousel> { product.images && product.images.map((item, i) => ( <img className="CarouselImage" key={item.url} src={item.url} alt={`${i} Slide`} /> )) } </Carousel> </div> </div> </Fragment> ); }; export default ProductDetails
Home.js代码
import { CgMouse } from "react-icons/cg"; import "./Home.css"; import Product from "./Product.js" import MetaData from "../layout/MetaData"; import { getProduct } from "../../actions/productAction"; import { useSelector, useDispatch} from "react-redux"; import Loader from "../layout/Loader/Loader"; import { useAlert } from "react-alert"; const Home = () => { const alert = useAlert(); const dispatch = useDispatch(); const { loading, error, products, productsCount } = useSelector( (state) => state.products); useEffect (() => { if (error) { return alert.error(error); } dispatch(getProduct()); }, [dispatch, error, alert]); return ( <Fragment> {loading ? ( <Loader /> ) : ( <Fragment> <MetaData title="ECOMMERCE" /> <div className="banner"> <p>Welcome to Ecommerce</p> <h1>FIND AMAZING PRODUCTS BELOW</h1> <a href="#container"> <button> Scroll <CgMouse /> </button> </a> </div> <h2 className="homeHeading">Featured Products</h2> <div className="container" id="container"> {products && products.map(product => ( <Product product={product} /> ))} </div> </Fragment> )} </Fragment> ); }; export default Home;
Product.js代码
import React from 'react'; import { Link } from 'react-router-dom'; import ReactStars from "react-rating-stars-component"; const Product = ({ product }) => { const options = { edit: false, color: "rgba (20,20,20,0.1)", activeColor: "tomato", size: window.innerWidth < 600 ? 20 : 25, value:product.ratings, isHalf:true }; return ( <Link className='productCard' to={`/product/${product._id}`}> <img src={product.images[0].url} alt={product.name} /> <p>{product.name}</p> <div> <ReactStars {...options} /> <span> ({product.numOfReviews} Reviews) </span> </div> <span>{`₹${product.price}`}</span> </Link> ); }; export default Product
报错信息
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method. at Carousel (http://localhost:3000/static/js/vendors~main.chunk.js:254237:5) at WithStyles (http://localhost:3000/static/js/vendors~main.chunk.js:7092:31) at div at div at ProductDetails (http://localhost:3000/static/js/main.chunk.js:1188:5) at Route (http://localhost:3000/static/js/vendors~main.chunk.js:259420:29) at Switch (http://localhost:3000/static/js/vendors~main.chunk.js:259622:29) at Router (http://localhost:3000/static/js/vendors~main.chunk.js:259039:30) at BrowserRouter (http://localhost:3000/static/js/vendors~main.chunk.js:257794:35) at App (http://localhost:3000/static/js/main.chunk.js:277:48) at Provider (http://localhost:3000/static/js/vendors~main.chunk.js:19626:23) at Provider (http://localhost:3000/static/js/vendors~main.chunk.js:255302:20) console.<computed> @ index.js:1 printWarning @ react-dom.development.js:67 error @ react-dom.development.js:43 warnAboutUpdateOnUnmountedFiberInDEV @ react-dom.development.js:23914 scheduleUpdateOnFiber @ react-dom.development.js:21840 enqueueSetState @ react-dom.development.js:12467 push../node_modules/react/cjs/react.development.js.Component.setState @ react.development.js:365 (anonymous) @ Carousel.js:313 setTimeout (async) setActive @ Carousel.js:312 componentDidMount @ Carousel.js:217 commitLifeCycles @ react-dom.development.js:20663 commitLayoutEffects @ react-dom.development.js:23426 callCallback @ react-dom.development.js:3945 invokeGuardedCallbackDev @ react-dom.development.js:3994 invokeGuardedCallback @ react-dom.development.js:4056 commitRootImpl @ react-dom.development.js:23151 unstable_runWithPriority @ scheduler.development.js:468 runWithPriority$1 @ react-dom.development.js:11276 commitRoot @ react-dom.development.js:22990 performSyncWorkOnRoot @ react-dom.development.js:22329 scheduleUpdateOnFiber @ react-dom.development.js:21881 updateContainer @ react-dom.development.js:25482 (anonymous) @ react-dom.development.js:26021 unbatchedUpdates @ react-dom.development.js:22431 legacyRenderSubtreeIntoContainer @ react-dom.development.js:26020 render @ react-dom.development.js:26103 (anonymous) @ index.js:16 ./src/index.js @ index.js:18 __webpack_require__ @ bootstrap:851 fn @ bootstrap:150 1 @ store.js:23 __webpack_require__ @ bootstrap:851 checkDeferredModules @ bootstrap:45 webpackJsonpCallback @ bootstrap:32 (anonymous) @ main.chunk.js:1 index.js:1 GET http://localhost:3000/product/%3Canonymous%3E 404 (Not Found)
解决方案
1. 修复详情页刷新空白问题
刷新空白是因为product初始状态为undefined,直接访问product.images导致组件崩溃,同时请求的商品ID为undefined(报错中的%3Canonymous%3E即<anonymous>),后端返回404无法加载数据。
修改ProductDetails.js,增加加载状态、错误处理,避免访问未定义属性:
import Carousel from "react-material-ui-carousel"; import "./ProductDetails.css"; import { useSelector, useDispatch } from "react-redux"; import { getProductDetails } from '../../actions/productAction'; import { useEffect, useState } from 'react'; import Loader from "../layout/Loader/Loader"; import { useAlert } from "react-alert"; import { Fragment } from "react"; const ProductDetails = ({match}) => { const dispatch = useDispatch(); const alert = useAlert(); const [isMounted, setIsMounted] = useState(true); const { product, loading, error} = useSelector( (state) => state.productDetails); useEffect( () => { setIsMounted(true); const fetchProduct = async () => { try { await dispatch(getProductDetails(match.params.id)); } catch (err) { if (isMounted) { alert.error(err.message || '加载商品详情失败'); } } }; fetchProduct(); return () => setIsMounted(false); }, [dispatch, match.params.id, alert]); if (error) { return <div className="error-message">{error}</div>; } return ( <Fragment> {loading ? <Loader /> : ( <div className="ProductDetails"> <div> {product?.images?.length > 0 ? ( <Carousel> {product.images.map((item, i) => ( <img className="CarouselImage" key={item.url} src={item.url} alt={`${i} Slide`} /> ))} </Carousel> ) : ( <div className="no-image">暂无商品图片</div> )} </div> </div> )} </Fragment> ); }; export default ProductDetails
2. 修复路由参数获取问题
如果使用React Router v6,match不再通过props传递,改用useParams()钩子获取商品ID:
import { useParams } from 'react-router-dom'; const ProductDetails = () => { const { id } = useParams(); const dispatch = useDispatch(); useEffect(() => { dispatch(getProductDetails(id)); }, [dispatch, id]); // 其余代码不变 };
同时确保路由配置正确:
// React Router v5 <Route path="/product/:id" component={ProductDetails} /> // React Router v6 <Route path="/product/:id" element={<ProductDetails />} />
3. 修复首页商品消失问题
首页商品消失大概率是Redux状态管理冲突导致,检查products和productDetails的reducer,确保状态互不干扰,错误处理不会清空商品列表:
products reducer示例:
import { ALL_PRODUCTS_REQUEST, ALL_PRODUCTS_SUCCESS, ALL_PRODUCTS_FAIL, CLEAR_ERRORS } from '../constants/productConstants'; export const productsReducer = (state = { products: [], loading: false, error: null }, action) => { switch(action.type) { case ALL_PRODUCTS_REQUEST: return { ...state, loading: true }; case ALL_PRODUCTS_SUCCESS: return { loading: false, products: action.payload.products, productsCount: action.payload.productsCount }; case ALL_PRODUCTS_FAIL: return { ...state, loading: false, error: action.payload }; case CLEAR_ERRORS: return { ...state, error: null }; default: return state; } };
productDetails reducer示例:
import { PRODUCT_DETAILS_REQUEST, PRODUCT_DETAILS_SUCCESS, PRODUCT_DETAILS_FAIL, CLEAR_ERRORS } from '../constants/productConstants'; export const productDetailsReducer = (state = { product: {}, loading: false, error: null }, action) => { switch(action.type) { case PRODUCT_DETAILS_REQUEST: return { ...state, loading: true }; case PRODUCT_DETAILS_SUCCESS: return { loading: false, product: action.payload }; case PRODUCT_DETAILS_FAIL: return { ...state, loading: false, error: action.payload }; case CLEAR_ERRORS: return { ...state, error: null }; default: return state; } };
另外在首页增加路由变化监听,确保返回首页时重新请求商品列表:
import { useLocation } from 'react-router-dom'; const Home = () => { const location = useLocation(); useEffect(() => { if (error) { return alert.error(error); } dispatch(getProduct()); }, [dispatch, error, alert, location.pathname]); };
4. 解决Carousel内存泄漏警告
警告是因为Carousel的自动播放定时器在组件卸载后仍运行,通过ref获取实例并在卸载时停止播放:
import { useRef } from 'react'; const ProductDetails = ({match}) => { const carouselRef = useRef(null); useEffect(() => { return () => { if (carouselRef.current) { carouselRef.current.stopAutoPlay(); } }; }, []); return ( // ... <Carousel ref={carouselRef}> {/* 图片列表 */} </Carousel> // ... ); };
内容的提问来源于stack exchange,提问作者Niyati
相关产品推荐
相关产品推荐

