React-Hook-Form+Chakra-UI集成国际手机号输入组件问题求助
解决React Hook Form + Chakra UI集成国际手机号输入的方案
方案1:修复react-international-phone的兼容问题
核心解决点
- 合并Ref冲突:通过工具函数合并RHF的register返回ref与react-international-phone的inputRef
- 同步onChange逻辑:使用RHF的
Controller组件统一处理值同步,避免钩子在回调内调用的限制
代码实现
首先实现合并Ref的工具函数:
const mergeRefs = (...refs) => { return (node) => { refs.forEach(ref => { if (typeof ref === 'function') { ref(node); } else if (ref != null) { ref.current = node; } }); }; };
然后编写兼容的手机号输入组件:
import { useFormContext, Controller } from 'react-hook-form'; import { usePhoneInput } from 'react-international-phone'; import { Input, Box, Select } from '@chakra-ui/react'; import { countries } from 'react-international-phone'; const InternationalPhoneInput = ({ name, ...rest }) => { const { control } = useFormContext(); return ( <Controller name={name} control={control} render={({ field: { onChange, value, ref } }) => { const { inputRef, phone, country, handlePhoneChange, handleCountryChange } = usePhoneInput({ defaultCountry: 'us', value: value || '', onChange: onChange, // 直接传递RHF的onChange同步值 }); // 合并两个ref,确保双方都能获取到input元素 const mergedRef = mergeRefs(ref, inputRef); return ( <Box display="flex" gap={2} alignItems="center"> <Select value={country.iso2} onChange={(e) => handleCountryChange(e.target.value)} width="30%" > {Object.values(countries).map((country) => ( <option key={country.iso2} value={country.iso2}> {country.name} (+{country.dialCode}) </option> ))} </Select> <Input {...rest} ref={mergedRef} value={phone} onChange={handlePhoneChange} /> </Box> ); }} /> ); };
关键说明
- 使用
Controller替代直接register,更灵活地对接第三方组件的状态逻辑 - 合并Ref确保RHF能正确追踪input元素,同时react-international-phone能正常处理输入格式
- 直接将RHF的
onChange传入usePhoneInput,避免在回调内调用钩子的限制
方案2:推荐开箱即用的替代库
1. react-phone-number-input
- 轻量无依赖,NextJS环境下性能优异
- 原生支持与RHF集成,可直接替换为Chakra-UI的Input组件
- 内置国家选择器和手机号格式校验
示例代码:
import { useFormContext, Controller } from 'react-hook-form'; import PhoneInput from 'react-phone-number-input'; import { Input, Box } from '@chakra-ui/react'; const RHFPhoneInput = ({ name, ...rest }) => { const { control } = useFormContext(); return ( <Box> <Controller name={name} control={control} render={({ field: { onChange, value } }) => ( <PhoneInput value={value} onChange={onChange} inputComponent={({ inputRef, ...props }) => ( <Input ref={inputRef} {...props} {...rest} /> )} /> )} /> </Box> ); };
2. 自定义验证方案(无UI库依赖)
如果不需要内置国家选择器,可使用libphonenumber-js配合RHF的验证规则:
import { useFormContext, Controller } from 'react-hook-form'; import { parsePhoneNumberFromString } from 'libphonenumber-js'; import { Input, FormErrorMessage } from '@chakra-ui/react'; const PhoneInput = ({ name, ...rest }) => { const { control } = useFormContext(); return ( <Controller name={name} control={control} rules={{ validate: (value) => { if (!value) return '请输入手机号'; const phoneNumber = parsePhoneNumberFromString(value); return phoneNumber?.isValid() || '请输入有效的国际手机号'; } }} render={({ field, fieldState: { error } }) => ( <Box> <Input {...field} {...rest} errorBorderColor="crimson" aria-invalid={!!error} placeholder="例如:+1234567890" /> {error && <FormErrorMessage>{error.message}</FormErrorMessage>} </Box> )} /> ); };
解决示例卡顿问题
你提到的浏览器卡顿问题,通常是因为渲染循环中重复创建函数/对象导致的额外渲染,可通过以下方式优化:
- 使用
useCallback包裹onChange等频繁调用的函数 - 用
useMemo包裹usePhoneInput的配置参数,避免每次渲染创建新对象
内容的提问来源于stack exchange,提问作者philolegein
相关产品推荐
相关产品推荐

