React Hooks实现类组件转函数组件并遍历数组查找商品id
商品详情页类组件转Hooks函数组件方案
需求说明
现有商品卡片列表页,点击任意商品卡片跳转对应商品详情页,需要将原有类组件改写为React Hooks函数组件。
用到的静态数据(products.json)
{ "shoes": { "items": [ { "id": 1, "name": "Massimo Dutti真皮运动鞋", "price": 135, "currency": "LEI", "image": "https://i.imgur.com/Rz8lVQa.jpg", "size": 45, "colour": "黑色", "material": "真皮", "brand": "Massimo Dutti", "description": "2019秋冬系列真皮运动鞋,原配白色鞋底,Massimo Dutti出品。" }, { "id": 2, "name": "Polo Ralph Lauren黑色休闲鞋", "price": 100, "currency": "LEI", "image": "https://i.imgur.com/Og3Oh24.jpg", "size": 45, "colour": "黑色", "material": "帆布", "brand": "Polo Ralph Lauren", "description": "2019春夏系列帆布休闲鞋,原配白色鞋底,Fashion Days出品。" }, { "id": 3, "name": "Massimo棕色真皮短靴", "price": 185, "currency": "LEI", "image": "https://i.imgur.com/zoqSu87.jpg", "size": 45, "colour": "棕色", "material": "真皮", "brand": "Massimo Dutti", "description": "2017秋冬系列棕色真皮短靴,Massimo Dutti出品。" } ], "image": "https://i.imgur.com/X0qldXO.jpg", "name": "鞋履", "description": "靴子、皮鞋、运动鞋,品质保证!" }, "sweaters": { "items": [ { "id": 4, "name": "修身高领羊绒毛衣", "price": 95, "currency": "LEI", "image": "https://i.imgur.com/6afzuEr.jpg", "size": "L", "colour": "棕色", "material": "羊绒", "brand": "DS Damat", "description": "2019秋冬系列羊绒修身毛衣,DS Damat出品。" }, { "id": 5, "name": "拼色毛衣", "price": 105, "currency": "LEI", "image": "https://i.imgur.com/s0VytKc.jpg", "size": "L", "colour": "拼色", "material": "棉", "brand": "McNeal", "description": "2019秋冬系列拼色毛衣,McNeal出品。" }, { "id": 6, "name": "保暖格纹羊毛毛衣", "price": 165, "currency": "LEI", "image": "https://i.imgur.com/BWGgzlK.jpg", "size": "L", "colour": "拼色", "material": "羊毛", "brand": "SELECTED", "description": "2019秋冬系列格纹羊毛保暖毛衣,SELECTED出品。" }, { "id": 7, "name": "纯羊毛毛衣", "price": 160, "currency": "LEI", "image": "https://i.imgur.com/dTcMbHT.jpg", "size": "L", "colour": "米白", "material": "羊毛", "brand": "Massimo Dutti", "description": "2019秋冬系列暗纹纯羊毛毛衣,Massimo Dutti出品。" } ], "image": "https://i.imgur.com/SZU7liE.jpg", "name": "毛衣", "description": "优雅修身毛衣,高端品质。" } }
原有类组件代码(Product.jsx)
import React from "react"; import products from "../utils/products.json"; class Product extends React.Component { constructor(props) { super(props); this.state = { product: [], }; } componentDidMount() { const { match } = this.props; const productId = match.params.productId; const categoryValues = Object.values(products); console.log(categoryValues); let currentProduct; categoryValues.forEach((category) => { const findResult = category.items.find((product) => { console.log(typeof productId, typeof product.id); return product.id === Number(productId); }); if (findResult) { currentProduct = findResult; } }); this.setState({ product: currentProduct }); } render() { const { product } = this.state; return ( <div className="product-page content-min-height container-fluid container-min-max-width"> <h1 className="my-5 h2">{product.name}</h1> <div className="product-info d-flex"> <div className="image-wrapper d-flex mr-5"> <img src={product.image} alt="商品展示" /> </div> <div className="product-details"> <p className="h3 text-danger"> {product.price} {product.currency} </p> </div> </div> </div> ); } } export default Product;
问题说明
尝试使用Hooks重构代码时,如果不借助组件生命周期遍历categoryValues,会出现category.items返回undefined的问题。
重构后的Hooks函数组件代码
以下方案兼容React Router v6版本,若使用v5版本可直接从props中获取match参数:
import React, { useState, useEffect } from "react"; import { useParams } from "react-router-dom"; // React Router v6 取路由参数用这个hook import products from "../utils/products.json"; const Product = () => { const [product, setProduct] = useState(null); const { productId } = useParams(); // 等价于原类组件的componentDidMount,依赖项增加productId可以支持参数变化时自动更新商品信息 useEffect(() => { const categoryValues = Object.values(products); let currentProduct; categoryValues.forEach((category) => { const findResult = category.items.find((item) => item.id === Number(productId)); if (findResult) currentProduct = findResult; }); setProduct(currentProduct); }, [productId]); // 增加加载态,避免首次渲染时product为null导致读取属性报错 if (!product) return <div>商品信息加载中...</div>; return ( <div className="product-page content-min-height container-fluid container-min-max-width"> <h1 className="my-5 h2">{product.name}</h1> <div className="product-info d-flex"> <div className="image-wrapper d-flex mr-5"> <img src={product.image} alt="商品展示" /> </div> <div className="product-details"> <p className="h3 text-danger"> {product.price} {product.currency} </p> {/* 你可以在这里扩展更多商品属性展示,比如尺码、材质、描述等 */} </div> </div> </div> ); }; export default Product;
问题原因说明
之前遇到的undefined问题是因为函数组件首次渲染时,useEffect里的逻辑还没有执行,product还是初始值,如果初始值设为数组,读取product.name自然会返回undefined,增加加载态判断即可解决。useEffect本身就等价于类组件生命周期的组合,这里的用法完全覆盖了原componentDidMount的功能,同时还支持路由参数变化时自动更新商品信息,比原类组件的扩展性更好。
内容的提问来源于stack exchange,提问作者Ionut.d
相关产品推荐
相关产品推荐

