You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 04:30:58