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

如何用React Hook Form将两个受控输入合并为单个值?

在React Hook Form中合并两个输入字段为单个值的实现方案

这里提供两种实用方案,帮你把下拉框(国家前缀)和输入框(手机号)的值合并成单个表单字段,无需在提交后手动处理。

方案一:利用watch和setValue实时合并

这种方式无需自定义组件,直接在表单内监听两个临时输入字段的变化,实时合并后更新目标字段。

import { useForm, useEffect } from 'react-hook-form';

function PhoneForm() {
  const { register, handleSubmit, watch, setValue, formState: { errors } } = useForm({
    defaultValues: {
      prefix: '+44',
      number: '',
      phone: '+44' // 初始合并值
    }
  });

  // 监听前缀和手机号输入的变化
  const prefix = watch('prefix');
  const number = watch('number');

  // 实时合并并更新目标字段
  useEffect(() => {
    // 可选:去除手机号中的空格、非数字字符等
    const cleanedNumber = number.replace(/[^0-9]/g, '');
    const mergedPhone = `${prefix}${cleanedNumber}`;
    // 更新phone字段,可选择触发验证
    setValue('phone', mergedPhone, { shouldValidate: true });
  }, [prefix, number, setValue]);

  const onSubmit = (data) => {
    // 提交时直接拿到合并后的phone值
    console.log('完整手机号:', data.phone);
    // 后续逻辑...
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <select {...register('prefix')}>
        <option value="+44">英国 (+44)</option>
        <option value="+1">美国 (+1)</option>
        <option value="+86">中国 (+86)</option>
      </select>

      <input
        type="tel"
        {...register('number', { required: '请输入手机号' })}
        placeholder="请输入手机号"
      />

      {errors.number && <p className="error">{errors.number.message}</p>}

      <button type="submit">提交</button>
    </form>
  );
}

方案二:自定义受控组件结合useController

如果需要在多个地方复用这个组合输入,推荐封装成独立组件,通过useController和表单上下文对接,对外暴露合并后的值。

import { useController, useFormContext, useEffect, useState } from 'react-hook-form';

// 自定义组合输入组件
const MergedPhoneInput = ({ name, rules }) => {
  const { control } = useFormContext();
  const { field, fieldState: { error } } = useController({
    name,
    control,
    defaultValue: '',
    rules
  });

  // 组件内部维护两个输入的状态
  const [prefix, setPrefix] = useState('+44');
  const [number, setNumber] = useState('');

  // 初始化时拆分已有值(用于编辑场景)
  useEffect(() => {
    if (field.value) {
      // 根据前缀列表匹配拆分,实际可根据需求调整逻辑
      const availablePrefixes = ['+44', '+1', '+86'];
      const matchedPrefix = availablePrefixes.find(p => field.value.startsWith(p));
      if (matchedPrefix) {
        setPrefix(matchedPrefix);
        setNumber(field.value.slice(matchedPrefix.length));
      }
    }
  }, [field.value]);

  // 合并值并同步到表单字段
  const syncMergedValue = () => {
    const cleanedNumber = number.replace(/[^0-9]/g, '');
    field.onChange(`${prefix}${cleanedNumber}`);
  };

  return (
    <div className="phone-input-group">
      <select 
        value={prefix} 
        onChange={(e) => {
          setPrefix(e.target.value);
          syncMergedValue();
        }}
      >
        <option value="+44">英国 (+44)</option>
        <option value="+1">美国 (+1)</option>
        <option value="+86">中国 (+86)</option>
      </select>

      <input
        type="tel"
        value={number}
        onChange={(e) => {
          setNumber(e.target.value);
          syncMergedValue();
        }}
        placeholder="请输入手机号"
      />

      {error && <p className="error">{error.message}</p>}
    </div>
  );
};

// 使用自定义组件的表单
function UserForm() {
  const { handleSubmit, control, formState: { errors } } = useForm({
    defaultValues: {
      phone: '+441234567890' // 编辑场景的初始值
    }
  });

  const onSubmit = (data) => {
    console.log('提交的完整手机号:', data.phone);
    // 后续逻辑...
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <MergedPhoneInput 
        name="phone" 
        rules={{ required: '请填写完整手机号' }} 
      />
      <button type="submit">提交</button>
    </form>
  );
}

关键注意点

  • 格式处理:可以根据需求添加输入格式化逻辑(比如去除非数字字符、自动补空格等),确保合并后的值符合预期格式。
  • 验证规则:验证可以直接加在合并后的phone字段上,或者在临时字段上添加,根据需求选择。
  • 编辑场景:方案二中的拆分逻辑要确保能正确解析已有值,回显到两个输入框中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:43:24