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

React电商项目产品详情页报错:Cannot read properties of undefined (reading 'params')

解决React Router v6中ProductDetails组件的params读取错误

问题描述

开发电商项目时,跳转至产品详情页后URL显示正常,但页面无法渲染,控制台抛出错误:

ProductDetails.js:22 Uncaught TypeError: Cannot read properties of undefined (reading 'params')

使用的react-router-dom版本为v6.4.1,相关代码如下:

App.js

import { BrowserRouter, Route, Routes, Link } from 'react-router-dom';
import Header from './components/layout/Header';
import Footer from './components/layout/Footer';
import Home from './components/layout/Home'
import './App.css';
import ProductDetails from './components/product/ProductDetails';

function App() {
  return( 
    <>
    <Header/>
    <Footer/>
    <Routes>
     
    <Route path='/' element={<Home/>} />
    <Route path='/product/:id' element={<ProductDetails/>} />

    <Route/>
  </Routes>
  </>
    )
}

export default App;

原ProductDetails.js

import React, {useEffect, Fragment} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getProductDetails, clearErrors } from '../../actions/productActions';
import { useAlert } from 'react-alert';
import Loader from '../layout/Loader';
import MetaData from '../layout/MetaData';


const ProductDetails = ({match}) => {
    
    const dispatch = useDispatch();
     const alert = useAlert();
     const {loading, error, product} = useSelector(state => state.productDetails)

 useEffect(()=>{
dispatch(getProductDetails(match.params.id))
if(error){
    alert.error(error);
    dispatch(clearErrors());
}

 },[dispatch, alert, error, match.params.id])

  return (
    <Fragment>
    {loading ? <Loader/> : (
        <Fragment>
            
        <div className="row f-flex justify-content-around">
            <div className="col-12 col-lg-5 img-fluid" id="product_image">
                <img src="https://i5.walmartimages.com/asr/1223a935-2a61-480a-95a1-21904ff8986c_1.17fa3d7870e3d9b1248da7b1144787f5.jpeg?odnWidth=undefined&odnHeight=undefined&odnBg=ffffff" alt="sdf" height="500" width="500"/>
            </div>

            <div className="col-12 col-lg-5 mt-5">
                <h3>"{product.name}</h3>
                <p id="product_id">Product # sklfjdk35fsdf5090</p>

                <hr/>

                <div className="rating-outer">
                    <div className="rating-inner"></div>
                </div>
                <span id="no_of_reviews">(5 Reviews)</span>

                <hr/>

                <p id="product_price">$108.00</p>
                <div className="stockCounter d-inline">
                    <span className="btn btn-danger minus">-</span>

                    <input type="number" className="form-control count d-inline" value="1" readOnly />

                    <span className="btn btn-primary plus">+</span>
                </div>
                 <button type="button" id="cart_btn" className="btn btn-primary d-inline ml-4">Add to Cart</button>

                <hr/>

                <p>Status: <span id="stock_status">In Stock</span></p>

                <hr/>

                <h4 className="mt-2">Description:</h4>
                <p>Binge on movies and TV episodes, news, sports, music and more! We insisted on 720p High Definition for this 32" LED TV, bringing out more lifelike color, texture and detail. We also partnered with Roku to bring you the best possible content with thousands of channels to choose from, conveniently presented through your own custom home screen.</p>
                <hr/>
                <p id="product_seller mb-3">Sold by: <strong>Amazon</strong></p>
                
                <button id="review_btn" type="button" className="btn btn-primary mt-4" data-toggle="modal" data-target="#ratingModal">
                            Submit Your Review
                </button>
                
                <div className="row mt-2 mb-5">
                    <div className="rating w-50">

                        <div className="modal fade" id="ratingModal" tabIndex="-1" role="dialog" aria-labelledby="ratingModalLabel" aria-hidden="true">
                            <div className="modal-dialog" role="document">
                                <div className="modal-content">
                                    <div className="modal-header">
                                        <h5 className="modal-title" id="ratingModalLabel">Submit Review</h5>
                                        <button type="button" className="close" data-dismiss="modal" aria-label="Close">
                                            <span aria-hidden="true">&times;</span>
                                        </button>
                                    </div>
                                    <div className="modal-body">

                                        <ul className="stars" >
                                            <li className="star"><i className="fa fa-star"></i></li>
                                            <li className="star"><i className="fa fa-star"></i></li>
                                            <li className="star"><i className="fa fa-star"></i></li>
                                            <li className="star"><i className="fa fa-star"></i></li>
                                            <li className="star"><i className="fa fa-star"></i></li>
                                        </ul>

                                        <textarea name="review" id="review" className="form-control mt-3">

                                        </textarea>

                                        <button className="btn my-3 float-right review-btn px-4 text-white" data-dismiss="modal" aria-label="Close">Submit</button>
                                    </div>
                                </div>
                            </div>
                        </div>

                    </div>
                        
            </div>

        </div>
        </div>
        </Fragment>
    )}
    </Fragment>
    
  )
}

export default ProductDetails

解决方案

React Router v6废除了v5中通过组件props传递match对象的方式,必须使用useParams钩子来获取URL参数。修改后的ProductDetails.js代码如下:

import React, {useEffect, Fragment} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getProductDetails, clearErrors } from '../../actions/productActions';
import { useAlert } from 'react-alert';
import Loader from '../layout/Loader';
import MetaData from '../layout/MetaData';
// 导入useParams钩子
import { useParams } from 'react-router-dom';


const ProductDetails = () => {
    // 使用useParams获取URL参数
    const params = useParams();
    
    const dispatch = useDispatch();
    const alert = useAlert();
    const {loading, error, product} = useSelector(state => state.productDetails)

    useEffect(()=>{
        // 从params中获取id
        dispatch(getProductDetails(params.id))
        if(error){
            alert.error(error);
            dispatch(clearErrors());
        }

    },[dispatch, alert, error, params.id])

  return (
    <Fragment>
    {loading ? <Loader/> : (
        <Fragment>
            
        <div className="row f-flex justify-content-around">
            <div className="col-12 col-lg-5 img-fluid" id="product_image">
                <img src="https://i5.walmartimages.com/asr/1223a935-2a61-480a-95a1-21904ff8986c_1.17fa3d7870e3d9b1248da7b1144787f5.jpeg?odnWidth=undefined&odnHeight=undefined&odnBg=ffffff" alt="sdf" height="500" width="500"/>
            </div>

            <div className="col-12 col-lg-5 mt-5">
                <h3>"{product.name}</h3>
                <p id="product_id">Product # sklfjdk35fsdf5090</p>

                <hr/>

                <div className="rating-outer">
                    <div className="rating-inner"></div>
                </div>
                <span id="no_of_reviews">(5 Reviews)</span>

                <hr/>

                <p id="product_price">$108.00</p>
                <div className="stockCounter d-inline">
                    <span className="btn btn-danger minus">-</span>

                    <input type="number" className="form-control count d-inline" value="1" readOnly />

                    <span className="btn btn-primary plus">+</span>
                </div>
                 <button type="button" id="cart_btn" className="btn btn-primary d-inline ml-4">Add to Cart</button>

                <hr/>

                <p>Status: <span id="stock_status">In Stock</span></p>

                <hr/>

                <h4 className="mt-2">Description:</h4>
                <p>Binge on movies and TV episodes, news, sports, music and more! We insisted on 720p High Definition for this 32" LED TV, bringing out more lifelike color, texture and detail. We also partnered with Roku to bring you the best possible content with thousands of channels to choose from, conveniently presented through your own custom home screen.</p>
                <hr/>
                <p id="product_seller mb-3">Sold by: <strong>Amazon</strong></p>
                
                <button id="review_btn" type="button" className="btn btn-primary mt-4" data-toggle="modal" data-target="#ratingModal">
                            Submit Your Review
                </button>
                
                <div className="row mt-2 mb-5">
                    <div className="rating w-50">

                        <div className="modal fade" id="ratingModal" tabIndex="-1" role="dialog" aria-labelledby="ratingModalLabel" aria-hidden="true">
                            <div className="modal-dialog" role="document">
                                <div className="modal-content">
                                    <div className="modal-header">
                                        <h5 className="modal-title" id="ratingModalLabel">Submit Review</h5>
                                        <button type="button" className="close" data-dismiss="modal" aria-label="Close">
                                            <span aria-hidden="true">&times;</span>
                                        </button>
                                    </div>
                                    <div className="modal-body">

                                        <ul className="stars" >
                                            <li className="star"><i className="fa fa-star"></i></li>
                                            <li className="star"><i className="fa fa-star"></i></li>
                                            <li className="star"><i className="fa fa-star"></i></li>
                                            <li className="star"><i className="fa fa-star"></i></li>
                                            <li className="star"><i className="fa fa-star"></i></li>
                                        </ul>

                                        <textarea name="review" id="review" className="form-control mt-3">

                                        </textarea>

                                        <button className="btn my-3 float-right review-btn px-4 text-white" data-dismiss="modal" aria-label="Close">Submit</button>
                                    </div>
                                </div>
                            </div>
                        </div>

                    </div>
                        
            </div>

        </div>
        </div>
        </Fragment>
    )}
    </Fragment>
    
  )
}

export default ProductDetails

修改要点

  1. 导入useParams钩子:import { useParams } from 'react-router-dom';
  2. 移除组件参数中的{match},改用const params = useParams();获取参数对象
  3. 将match.params.id替换为params.id,包括useEffect的依赖项

内容的提问来源于stack exchange,提问作者Tort

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 21:15:33