如何用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
相关产品推荐
相关产品推荐

