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

如何在TypeScript中整合intl-tel-input与React Hook Form Controller并实现错误聚焦?

解决方案:整合intl-tel-input React组件与React Hook Form Controller并实现自动聚焦

要解决这个问题,核心是让React Hook Form(RHF)的ref正确指向IntlTelInput内部的原生<input>元素,而非组件实例或覆盖组件内部的ref处理逻辑。以下是具体实现步骤:

问题根源

  • 直接将RHF的ref传给IntlTelInput组件的ref属性时,ref指向的是组件实例而非原生input元素,调用focus()自然会报错elm.focus is not a function。
  • 通过inputProps直接赋值ref会覆盖组件内部对input元素的ref绑定,导致组件无法正常渲染UI(如国旗选择器)。

正确实现方式

使用forwardRef转发ref,并通过IntlTelInput组件实例的getInput()方法获取原生input元素,将其关联到RHF传入的ref上:

1. 封装PhoneInput子组件(带forwardRef)

import React, { forwardRef, useEffect, useRef } from 'react';
import IntlTelInput from 'intl-tel-input-react';
import 'intl-tel-input/build/css/intlTelInput.css';

// 定义组件props类型
type PhoneInputProps = Omit<React.ComponentProps<typeof IntlTelInput>, 'ref'>;

const PhoneInput = forwardRef<HTMLInputElement, PhoneInputProps>((props, externalRef) => {
  // 保存IntlTelInput组件实例的ref
  const itiInstanceRef = useRef<IntlTelInput | null>(null);

  useEffect(() => {
    if (itiInstanceRef.current && externalRef) {
      // 获取组件内部的原生input元素
      const inputElement = itiInstanceRef.current.getInput();
      
      // 将原生input元素关联到外部传入的ref(兼容函数式ref和对象式ref)
      if (typeof externalRef === 'function') {
        externalRef(inputElement);
      } else if (externalRef) {
        externalRef.current = inputElement;
      }
    }
  }, [externalRef]);

  return (
    <IntlTelInput
      ref={itiInstanceRef}
      // 传递其他props,不要在inputProps中覆盖ref
      {...props}
    />
  );
});

PhoneInput.displayName = 'PhoneInput';
export default PhoneInput;

2. 在父组件中使用Controller

import { Controller, useForm } from 'react-hook-form';
import PhoneInput from './PhoneInput';

type FormValues = {
  phoneE164: string;
};

const MyForm = () => {
  const { control, handleSubmit, formState: { errors } } = useForm<FormValues>();

  const onSubmit = (data: FormValues) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        control={control}
        name="phoneE164"
        rules={{ required: '请输入手机号码' }}
        render={({ field }) => (
          <div>
            <PhoneInput
              {...field}
              preferredCountries={['cn']}
              initialCountry="cn"
            />
            {errors.phoneE164 && <span style={{ color: 'red' }}>{errors.phoneE164.message}</span>}
          </div>
        )}
      />
      <button type="submit">提交</button>
    </form>
  );
};

export default MyForm;

原理说明

  • forwardRef允许子组件接收父组件传入的ref,并将其绑定到内部的原生input元素上。
  • 通过itiInstanceRef获取IntlTelInput的组件实例,调用getInput()方法拿到真实的input DOM元素,确保RHF的ref指向正确的元素,调用focus()不会出错。
  • 没有覆盖inputProps中的ref,保留了组件内部的ref处理逻辑,因此UI不会出现异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 16:54:59