React重渲染引发订单组件更新性能问题及优化需求
解决订单组件更新时所有商品重渲染的性能问题
我仔细看了你的代码,问题的根源其实出在几个关键的地方,导致memo没有起到预期的缓存作用,每次修改商品数量时所有OrderItem都被迫重渲染。咱们一步步来解决:
1. 修复Order组件中回调函数的依赖项(核心问题)
你在Order组件里用了useCallback但没有正确设置依赖,导致每次Order渲染时这些回调函数都会生成新的引用。而OrderItem虽然用了memo,但因为接收的onQuantityChange、onRemove等props每次都是新的,所以会触发所有组件的重渲染。
修改Order组件的回调函数,补充正确的依赖:
import OrderItem from "./OrderItem"; import { useEffect, memo, useCallback } from "react"; const Order = ({ order, products, updateOrderProducts, updateOrder }) => { const handleOrderChange = useCallback((propertyName, value) => { let orderCopy = { ...order }; orderCopy[propertyName] = value; updateOrder(orderCopy); }, [order, updateOrder]); // 补充依赖:order和updateOrder const deleteProduct = useCallback((index) => { let productsCopy = [...products]; productsCopy = productsCopy.filter((_, idx) => idx !== index); // 用索引判断更可靠 updateOrderProducts(productsCopy); }, [products, updateOrderProducts]); // 补充依赖:products和updateOrderProducts const handleOrderItemRemove = useCallback((index) => { deleteProduct(index); }, [deleteProduct]); // 补充依赖:deleteProduct const handleQuantityChange = useCallback((index, quantity) => { let productsCopy = [...products]; productsCopy[index]["quantity"] = quantity; updateOrderProducts(productsCopy); }, [products, updateOrderProducts]); // 补充依赖:products和updateOrderProducts return ( <div className="d-flex px-2 flex-grow-1 mb-1"> {products.map((product, idx) => ( <OrderItem product={product} key={product.name} // 替换index为唯一标识,实际项目建议用商品ID index={idx} onRemove={handleOrderItemRemove} onQuantityChange={handleQuantityChange} updateProduct={handleOrderChange} /> ))} </div> ); }; export default memo(Order);
这里的关键改动:
- 给每个
useCallback补充了正确的依赖,确保只有当依赖变化时才生成新的函数引用 - 修复了
deleteProduct里的过滤逻辑,用索引判断比直接比较对象更可靠 - 把
key从idx换成了product.name(实际业务中建议用商品唯一ID),避免删除商品时后续组件的key变化导致不必要的重渲染
2. 优化OrderItem组件的内部逻辑
你的OrderItem里有一些冗余的逻辑和错误的useMemo用法,也会影响渲染效率:
import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; import AddCircleIcon from "@mui/icons-material/AddCircle"; import { memo, useMemo, useState, useEffect } from "react"; const OrderItem = ({ product, index, onQuantityChange }) => { console.log("remount", product.name); // 打印商品名验证只有修改的商品重渲染 const [itemQuantity, setItemQuantity] = useState(product.quantity); // 同步外部传入的product.quantity到本地状态 useEffect(() => { setItemQuantity(product.quantity); }, [product.quantity]); // 只监听product.quantity的变化 const incrementQuantity = () => { onQuantityChange(index, itemQuantity + 1); }; const decrementQuantity = () => { itemQuantity > 1 && onQuantityChange(index, itemQuantity - 1); }; // 正确使用useMemo缓存渲染结果 const productInfo = useMemo(() => { const price = +product.sellPrice.toFixed(2); const total = +(price * product.quantity).toFixed(2); return ( <div className={`col-9 col-xl-10 border rounded-start p-1 `}> {product.name} <div className="justify-content-around d-flex"> {"Price:" + price} {" Quantity:" + product.quantity} {" Total:" + total} </div> </div> ); }, [product.name, product.sellPrice, product.quantity]); // 补充依赖,仅在必要时重新生成JSX const quantityColumn = ( <div> <AddCircleIcon onClick={incrementQuantity} /> {itemQuantity} <RemoveCircleIcon onClick={decrementQuantity} /> </div> ); return ( <div style={{ marginBottom: "25px" }}> {productInfo} {quantityColumn} </div> ); }; export default memo(OrderItem);
这里的关键优化:
- 合并了重复的
useEffect,只监听product.quantity的变化,减少不必要的状态更新 - 修正了
useMemo的用法:直接返回JSX,补充正确的依赖缓存渲染结果 - 简化了
decrementQuantity的逻辑,提升可读性
3. 优化OrderingScreen中的状态更新逻辑
你的updateOrderProducts和updateOrder函数可以进一步优化,避免不必要的对象复制:
const updateOrderProducts = useCallback( (products) => { setOrdersData(prev => { // 函数式更新,避免依赖ordersData,让函数引用更稳定 const newOrders = [...prev]; newOrders[0] = { ...newOrders[0], order: { ...newOrders[0].order, orderDetails: [...products] } }; return newOrders; }); }, [] // 无需依赖ordersData ); const updateOrder = useCallback( (order) => { setOrdersData(prev => { const newOrders = [...prev]; newOrders[0] = { ...newOrders[0], order: { ...order } }; return newOrders; }); }, [] // 去掉ordersData依赖 );
用函数式更新的好处是,不需要依赖ordersData,这样updateOrderProducts和updateOrder的引用会一直稳定,不会因为ordersData变化而重新生成,进一步减少子组件的重渲染触发条件。
做完这些修改后,你会发现修改单个商品数量时,只有对应的OrderItem会打印"remount",其他商品组件不会重渲染,性能会有明显提升。
内容的提问来源于stack exchange,提问作者Marwan-Ezzat
相关产品推荐
相关产品推荐

