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

基于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>
);

为什么这样能解决问题?

  1. 每个商品的数量独立存储,输入框之间不会互相影响,子组件也能正确计算单个商品的小计;
  2. 父组件直接拥有所有商品的数量数据,不需要从子组件“收集”小计,直接通过遍历计算就能得到总计,逻辑更清晰。

另外你提到“如果能拿到所有输入的数组就容易计算”,现在用对象存储的方式其实和数组等价,遍历的时候就能逐个获取每个商品的数量,完全满足计算需求。

备注:内容来源于stack exchange,提问作者kishore kumar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 07:08:08