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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 11:47:52