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

如何使用use hook form计算总价并实时同步至禁用输入框?

解决实时更新总价输入框的问题

要实现会员类型或单价变化时实时更新总价,核心是监听这两个字段的变化,实时计算并更新表单的total字段,同时让组件重新渲染显示最新值。

关键修改点:

  • 从useForm中解构出setValue方法,用于更新表单字段
  • 使用useEffect监听membershipType和price的变化,实时计算总价并更新total
  • 处理输入框的字符串转数字问题,避免计算出错
  • 正确渲染禁用的总价输入框

修改后的完整代码:

import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import ValidationSchema from './your-validation-schema-path'; // 替换为你的验证文件路径

type Member = {
  assignedBy: string
  name: string
  lastName: string
  gender: string
  phone: number
  membershipType: string
  startingDate: string
  price: number
  total: number
}

const AddMember = () => {
  const form = useForm<Member>({
    defaultValues: {
      assignedBy: 'admin',
      name: '',
      lastName: '',
      gender: '',
      membershipType: '',
      startingDate: '',
      total: 0,
      price: 0 // 给price设置默认数字值,避免初始为undefined
    },
    resolver: yupResolver(ValidationSchema)
  });
  const { register, control, handleSubmit, formState, watch, setValue } = form;
  const { isDirty, errors } = formState;
  const watchMembershipType = watch('membershipType');
  const watchPrice = watch('price');

  // 实时计算总价的逻辑
  useEffect(() => {
    // 处理price为字符串的情况,转成数字,默认0
    const numericPrice = typeof watchPrice === 'string' ? parseFloat(watchPrice) || 0 : watchPrice || 0;
    let calculatedTotal = 0;

    switch (watchMembershipType) {
      case '1 month':
        calculatedTotal = numericPrice;
        break;
      case '3 months':
        calculatedTotal = numericPrice * 3 * 0.9; // 简化计算:10%折扣即乘以0.9
        break;
      case '1 year':
        calculatedTotal = numericPrice * 12 * 0.8; // 20%折扣即乘以0.8
        break;
      default:
        calculatedTotal = 0;
    }

    // 更新表单的total字段
    setValue('total', calculatedTotal, { shouldValidate: false }); // 不需要验证total字段
  }, [watchMembershipType, watchPrice, setValue]); // 依赖变化时触发

  const onSubmit = (data: Member) => {
    // 提交时total已经是最新值,无需再计算,直接使用即可
    console.log(data);
  }

  return (
    <div className='add-member'>
      {/* 其他表单代码 */}
      <div className='input' id='price-input'>
        <label>Price </label>
        <input 
          {...register('price', { valueAsNumber: true })} // 使用valueAsNumber自动转数字
          placeholder='$ (per month)' 
          className={errors.price?.message ? 'error' : ''} 
        />
      </div>
      <div className='input' id='price-total'>
        <label>Total</label>
        <input 
          {...register('total')} 
          disabled 
          className={errors.total?.message ? 'error' : ''} 
        />
      </div>
      <button type='submit' disabled={!isDirty}>Submit</button>
      <DevTool control={control} />
    </div>
  )
}

export default AddMember

代码说明:

  1. setValue的使用:通过setValue更新表单的total字段,这样表单内部状态会同步,输入框会自动显示最新值。
  2. useEffect监听变化:当membershipType或price改变时,自动触发计算并更新总价。
  3. valueAsNumber处理输入:给price的register添加valueAsNumber,自动将输入的字符串转为数字,避免后续计算出现NaN。
  4. 简化折扣计算:把(price*3)-(price*3*0.1)简化为price*3*0.9,逻辑更清晰。
  5. 默认值处理:给price设置默认值0,避免初始状态下watchPrice为undefined导致计算错误。

这样修改后,当用户选择会员类型或修改单价时,总价输入框会实时更新显示计算结果,提交时data.total也会是最新的正确值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:22:43