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

如何使用useFieldArray获取数组项的价格与数量值并计算总价?

解决React Hook Form useFieldArray动态表单总价计算问题

问题核心

你遇到的item is not defined错误是因为React Hook Form不会自动暴露item变量,表单字段值需要通过官方提供的API(如watch、getValues)获取,同时代码中存在字段路径不匹配的问题:useFieldArray的name是"items",但register时用的是item.${i}.xxx,导致表单值无法正确关联。

修复步骤

1. 修正字段注册路径

将所有register的字段名从item.${i}.xxx改为items.${i}.xxx,同时修正错误提示的路径,并且合并register的配置项(之前分开写的配置会被覆盖):

// 修正Item Name的register和错误提示
<StyledInput 
  type="text" 
  id="itemName" 
  {...register(`items.${i}.name`, { required: "Can't be empty" })} 
/>
<Error>{errors?.['items']?.[i]?.['name']?.['message']}</Error>

// 修正Quantity的register和错误提示
<StyledInput 
  type="text" 
  id="itemsQuantity" 
  {...register(`items.${i}.quantity`, { valueAsNumber: true, required: "Can't be empty" })} 
/>
<Error>{errors?.['items']?.[i]?.['quantity']?.['message']}</Error>

// 修正Price的register和错误提示
<StyledInput 
  type="text" 
  id="itemPrice" 
  {...register(`items.${i}.price`, { valueAsNumber: true, required: "Can't be empty" })} 
/>
<Error>{errors?.['items']?.[i]?.['price']?.['message']}</Error>

2. 用watch实时计算总价

watch可实时监听表单字段变化,适合需要实时更新总价的场景:

// 从useForm中解构watch
const { control, register, watch, formState: { errors } } = useForm();
// 监听整个items数组
const items = watch("items");

// 渲染总价时计算
<StyledField>
  {Intl.NumberFormat('en-GB', { style: "currency", currency: 'GBP' }).format(
    // 处理空值避免NaN
    (items?.[i]?.price || 0) * (items?.[i]?.quantity || 0)
  )}
</StyledField>

3. 用getValues获取当前值(非实时场景)

如果不需要实时更新,仅在需要时获取当前值,可使用getValues:

// 从useForm中解构getValues
const { control, register, getValues, formState: { errors } } = useForm();

// 渲染总价时计算
<StyledField>
  {Intl.NumberFormat('en-GB', { style: "currency", currency: 'GBP' }).format(
    (getValues(`items.${i}.price`) || 0) * (getValues(`items.${i}.quantity`) || 0)
  )}
</StyledField>

完整修正后的渲染代码片段

{fields.map((field, i) =>
  <div key={field.id} tw="grid grid-rows-2 grid-cols-[64px_100px_100px_auto] tablet:flex tablet:flex-row gap-x-6 gap-x-4 gap-y-4 mb-4">
    <div tw="flex flex-col col-span-4 tablet:w-[220px]">
      <label htmlFor="itemName" tw="text-secondary">Item Name</label>
      <StyledInput type="text" id="itemName" {...register(`items.${i}.name`, { required: "Can't be empty" })} />
      <Error>{errors?.['items']?.[i]?.['name']?.['message']}</Error>
    </div>
    <div tw="flex flex-col row-start-2 col-start-1 tablet:w-12">
      <label htmlFor="itemsQuantity" tw="text-secondary">Qty.</label>
      <StyledInput type="text" id="itemsQuantity" {...register(`items.${i}.quantity`, { valueAsNumber: true, required: "Can't be empty" })} />
      <Error>{errors?.['items']?.[i]?.['quantity']?.['message']}</Error>
    </div>
    <div tw="flex flex-col row-start-2 col-start-2 tablet:w-20">
      <label htmlFor="itemPrice" tw="text-secondary">Price</label>
      <StyledInput type="text" id="itemPrice" {...register(`items.${i}.price`, { valueAsNumber: true, required: "Can't be empty" })} />
      <Error>{errors?.['items']?.[i]?.['price']?.['message']}</Error>
    </div>
    <div tw="flex flex-col row-start-2 col-start-3 tablet:w-20">
      <label htmlFor="itemPrice" tw="text-secondary">Total</label>
      <StyledField>
        {Intl.NumberFormat('en-GB', { style: "currency", currency: 'GBP' }).format(
          (items?.[i]?.price || 0) * (items?.[i]?.quantity || 0)
        )}
      </StyledField>
    </div>
    <div onClick={() => remove(i)} tw="flex flex-col pt-4 row-start-2 col-start-4 tablet:w-auto place-content-center cursor-pointer">
      <Image src="/assets/icon-delete.svg" width={12} height={16} alt="recyclingBin" />
    </div>
  </div>
)}

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 04:45:06