React-Router-DOM v6动态路由问题:useParams获取id为undefined
问题解决:React-Router-DOM v6 动态路由id获取undefined
问题现象
使用React和React-Router-DOM v6搭建电商商品详情页时,从商品列表页点击卡片跳转后,详情页通过useParams()获取的id为undefined;但手动在浏览器URL中输入/products/17这类带id的地址时,控制台能正常获取id并请求到数据。
错误原因分析
- Products组件的Link配置错误:你将整个商品列表包裹在单个
<Link>标签中,且链接地址使用products.id——但products是数组,不存在id属性,导致生成的链接为/products/undefined,跳转后详情页无法获取有效id。 - 路由顺序问题:App.js中
path="/*"的404路由放在了动态路由/products/:id前面,React-Router会优先匹配第一个符合规则的路由,导致动态路由被404路由拦截(手动输入URL暂时生效属于偶然情况)。
修复方案
1. 修正Products组件的Link位置与参数
将<Link>标签移至每个商品卡片内部,使用当前遍历项的item.id作为动态参数:
修改后的Products组件核心代码:
// 移除外层包裹整个列表的Link <div className="p3-5 prods"> {products.map((item, index) => { const { brand, image_url, price, rating, title, id } = item; // 解构出当前商品的id return ( <Link key={index} to={`/products/${id}`}> {/* 每个卡片对应独立的跳转链接 */} <div className="product-container"> <img src={image_url} alt={title} className="map-image" /> {/* 补充图片alt文本 */} <div className="card-body"> <h5 className="card-title text-center pt-2"> {title.substr(0, 30)} </h5> <p className="card-text text-center">{`by ${brand}`}</p> <p className="text-center price">{`₹ ${price}`}</p> <p className="ratings text-center">{`⭐ ${rating}`}</p> <div className="btns text-center"> <button className="btns"> Add to Cart{<ShoppingCart />}{" "} </button> </div> </div> </div> </Link> ); })} </div>
2. 调整App.js的路由顺序
将404路由移至所有路由的最后,确保动态路由能被正确匹配:
修改后的App.js路由部分:
<Routes> <Route path="/" element={<Protected Comp={Home} />} /> <Route path="/login" element={<Login />} /> <Route path="/carts" element={<Protected Comp={Carts} />} /> <Route path="/products" element={<Protected Comp={Products} />} /> <Route path="/products/:id" element={<ProductsDetailPage />} /> {/* 动态路由放在404路由之前 */} <Route path="/*" element={<Page404 />} /> {/* 404路由作为兜底,放在最后 */} </Routes>
3. 可选优化:ProductsDetailPage组件
添加加载状态与空值判断,避免无效请求:
function ProductsDetailPage() { const [product, setProduct] = useState(null); // 单个商品数据,变量名改为product更准确 const [loading, setLoading] = useState(true); const { id } = useParams(); const getProductDetails = async () => { if (!id) return; // 无有效id时不发起请求 setLoading(true); try { const jwtToken = Cookies.get("jwt_token"); const apiUrl = `https://apis.ccbp.in/products/${id}`; const options = { headers: { Authorization: `Bearer ${jwtToken}` }, method: "GET", }; const response = await fetch(apiUrl, options); const data = await response.json(); setProduct(data.product); // API返回的是单个商品对象,而非数组 } catch (error) { console.error("Error fetching data:", error); } finally { setLoading(false); } }; useEffect(() => { getProductDetails(); }, [id]); if (loading) return <div>Loading...</div>; if (!product) return <div>Product not found</div>; return ( <> <h1>{product.title}</h1> <img src={product.image_url} alt={product.title} /> <p>Brand: {product.brand}</p> <p>Price: ₹{product.price}</p> <p>Rating: ⭐ {product.rating}</p> {/* 其他商品详情字段 */} </> ); }
验证效果
修改完成后,点击商品卡片会生成/products/[id]格式的正确链接,跳转至详情页后,useParams()能正常获取id并请求到对应商品数据。
内容的提问来源于stack exchange,提问作者Saurabh
相关产品推荐
相关产品推荐

