基于map渲染表单输入+子组件计算小计的场景下,如何计算所有项的总计金额
基于map渲染表单输入+子组件计算小计的场景下,如何计算所有项的总计金额
看起来你现在遇到了两个核心问题:一是当前的状态管理导致所有输入框共用同一个qty值,输入会互相影响;二是没法汇总所有子组件计算出的小计得到总计。我来一步步帮你解决:
第一步:重构状态管理,单独跟踪每个商品的数量
你原来用{ qty: 0 }的状态显然不对,因为每个商品的数量应该是独立的。我们改成用商品的id作为键来存储每个商品的数量,这样能精准区分每个输入项:
import { useState, useEffect } from 'react'; // ...其他代码 const PriceList = () => { // 用对象存储每个商品的数量,键是item.id const [quantities, setQuantities] = useState({}); // 初始化所有商品的数量为0(组件挂载/ListItems变化时执行) useEffect(() => { const initialQuantities = {}; ListItems.forEach(section => { section.items.forEach(item => { initialQuantities[item.id] = 0; }); }); setQuantities(initialQuantities); }, [ListItems]); // 修改输入事件处理函数,针对单个商品id更新数量 const handleInputChange = (e, itemId) => { e.preventDefault(); // 确保输入是数字,非数字则设为0 const inputValue = parseInt(e.target.value) || 0; setQuantities(prevState => ({ ...prevState, [itemId]: inputValue })); }; // ...后续代码 };
第二步:更新表单输入和子组件的props
现在每个输入框要绑定对应商品的数量,并且把商品id传递给事件处理函数:
// 在section.items.map的循环里: <tr key={item.id} > <td>{item.productName}</td> <td>{item.unit}</td> <td>{item.price}</td> <td> <FormElement type="number" name={`quantity-${item.id}`} value={quantities[item.id] || 0} handleChange={(e) => handleInputChange(e, item.id)} /> </td> <Subtotal class="subtotal" price={item.price} quantity={quantities[item.id] || 0} /> </tr>
第三步:在父组件计算总计金额
现在我们已经有了所有商品的数量数据,直接遍历所有商品,累加每个商品的价格×数量就能得到总计:
// 定义计算总计的函数 const calculateTotal = () => { let total = 0; ListItems.forEach(section => { section.items.forEach(item => { const qty = quantities[item.id] || 0; total += item.price * qty; }); }); return total; }; // 在表格里添加总计行,比如放在tbody之后的tfoot里 return ( <div> <Table bordered hover> <thead> {/* 原来的表头代码不变 */} <tr> <th>PRODUCT NAME</th> <th>UNIT</th> <th>PRICE</th> <th>QUANTITY</th> <th>SUB TOTAL</th> </tr> </thead> <tbody> {/* 原来的列表渲染代码不变 */} {ListItems.map((section) => <React.Fragment key={section.id}> <tr> <td>{section.name}</td> </tr> {section.items.map((item) => <tr key={item.id} > {/* 这里放刚才修改后的输入和Subtotal组件代码 */} </tr> )} </React.Fragment> )} </tbody> {/* 新增总计行 */} <tfoot> <tr> <td colSpan="4" align="right"><strong>总计:</strong></td> <td><strong>{calculateTotal()}</strong></td> </tr> </tfoot> </Table> </div> );
为什么这样能解决问题?
- 每个商品的数量独立存储,输入框之间不会互相影响,子组件也能正确计算单个商品的小计;
- 父组件直接拥有所有商品的数量数据,不需要从子组件“收集”小计,直接通过遍历计算就能得到总计,逻辑更清晰。
另外你提到“如果能拿到所有输入的数组就容易计算”,现在用对象存储的方式其实和数组等价,遍历的时候就能逐个获取每个商品的数量,完全满足计算需求。
备注:内容来源于stack exchange,提问作者kishore kumar
相关产品推荐
相关产品推荐

