使用“反应电话”输入的蚂蚁设计。

发布于 2025-02-12 08:32:27 字数 747 浏览 0 评论 0 原文

我试图在react-phone-number输入中使用蚂蚁设计的输入组件,如本示例中所述: https://catamphetamine.gitlab.io/reaeact-phone-number-input/ (自定义< input/input/>

主要问题是蚂蚁设计的输入IS一个复合组件,我相信这会引起一些问题。 只有在使用ant Design的输入时,我才会收到以下错误:

Unhandled Runtime Error
TypeError: element.hasAttribute is not a function

我的代码看起来像:

import PhoneInput from 'react-phone-number-input'
import Input from 'ant-design'

<Form.Item name="phone">
 <PhoneInput inputComponent={Input} />
</Form.Item>

有什么方法我可以从复合配件中导出输入组件库?

I'm trying to use ant design's input component inside react-phone-number-input, as explained in this example: https://catamphetamine.gitlab.io/react-phone-number-input/ (Custom <input/>)

The main issue is that ant design's input is a CompoundedComponent and I believe it's causing some issues.
Only when using ant design's input I'm getting the following error:

Unhandled Runtime Error
TypeError: element.hasAttribute is not a function

My code looks like this:

import PhoneInput from 'react-phone-number-input'
import Input from 'ant-design'

<Form.Item name="phone">
 <PhoneInput inputComponent={Input} />
</Form.Item>

Is there any way I can maybe export only the Input component from the CompoundedComponent so it works with the react-phone-number-input library?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

望她远 2025-02-19 08:32:27

React-Phone-number输入如果要使用自定义输入组件,则需要输入字段参考。 ANTD输入字段没有将引用引用到DOM输入元素,而是将其保持在其自己的组件级别,并在上方创建元素。

我们可以通过创建自定义组件并传递DOM输入字段参考而不是原始ANTD引用来使用使用ImparativeHandle。

我的自定义组件可能看起来像这样。

const MyInput = React.forwardRef((props , ref) => {
  const inputRef = useRef(null);

  useImperativeHandle(
    ref,
    () => {
      return inputRef.current.input;
    },
    []
  );
  return <Input {...props} ref={inputRef} />;
});

以下是如何

<PhoneInput
      value={value}
      onChange={onChange}
      placeholder="Enter phone number"
      className="ant-input"
      defaultCountry="IN"
      international={true}
      limitMaxLength={true}
      countries={['IN']}
      inputComponent={MyInput}
      {...props}
    />

用于完整工作演示的方式查看 demo

react-phone-number-input require input field reference to be pass if you want to use custom Input component. Antd Input field is not forwarding reference to dom input element instead it keep it at its own component level and that creating above element.

We may use useImparativeHandle by creating custom component and pass dom input field reference instead of original AntD reference.

My custom component may look like this.

const MyInput = React.forwardRef((props , ref) => {
  const inputRef = useRef(null);

  useImperativeHandle(
    ref,
    () => {
      return inputRef.current.input;
    },
    []
  );
  return <Input {...props} ref={inputRef} />;
});

Below is how you can use

<PhoneInput
      value={value}
      onChange={onChange}
      placeholder="Enter phone number"
      className="ant-input"
      defaultCountry="IN"
      international={true}
      limitMaxLength={true}
      countries={['IN']}
      inputComponent={MyInput}
      {...props}
    />

For full working demo check out Demo

哥,最终变帅啦 2025-02-19 08:32:27

为了使其与ANTD合作,我可以建议您将自己的组件放在您的React应用程序中使用它们的内部。让我们称此组件 phonenumberinput.tsx
您可以使用他们的功能进行格式和解析电话号码输入,例如 formatphonenumber,parsephoneNumber,getCountries ,但是请查看他们在NPM模块中使用的依赖项。从我的角度来看,最好直接使用 libphoneNeNumber-js

import React from 'react'
import { Form, Button, Input, Select } from 'antd'

import { formatPhoneNumber, parsePhoneNumber, getCountries } from 'react-phone-number-input'
import { CountryCode } from 'libphonenumber-js/types'

export const PhoneNumberInput = () => {
  const [form] = Form.useForm()


  const countryOptions: { value: string, label: JSX.Element}[] = getCountries().map((ZZ) => {
    return {
        value: ZZ,
        label: <span> <img src={`images/flags/${ZZ}.svg`} className='country-flag-icon'/> {ZZ}</span> ,
    }
  })

  countryOptions.unshift({
    value: 'unknown',
    label: <span><img src='images/flags/unknown.svg'  className='country-flag-icon'/></span>,
  })


  function numberInputChanged(phone: any){
    const parsed = parsePhoneNumber(phone, form.getFieldValue('countryCode')) 
    form.setFieldsValue({
      phoneNumber: parsed && formatPhoneNumber(parsed.number)||phone,
      countryCode: (parsed && parsed.country) || 'unknown',
    })
  }


  function selectCountry(ZZ: any){
    form.setFieldValue('countyCode', ZZ)
    const phoneNumber = form.getFieldValue('phoneNumber')
    if(!phoneNumber) return
    const parsed = parsePhoneNumber(phoneNumber, ZZ)
    parsed && parsed.number && form.setFieldValue('phoneNumber', parsed && formatPhoneNumber(parsed.number))
    
  }

  function getPhoneNumber(phone: {countryCode: CountryCode | 'unknown', phoneNumber: string}){
    if(phone.countryCode === 'unknown') console.log('not formatted',phone.phoneNumber)
    else console.log('formatted:', parsePhoneNumber(phone.phoneNumber, phone.countryCode)?.number)
  }

  return (
    <>
    <Button
        form="test-phone-form"
        htmlType="submit"
    >
        finish
    </Button>
    <Form 
        id="test-phone-form"
        form={form}
        onFinish={getPhoneNumber}
    >
        <Form.Item name={"phoneNumber"} >
            <Input
            placeholder="Your phone number"
            onChange={(e) => numberInputChanged(e.target.value)}
            addonBefore={
                <Form.Item name={"countryCode"} style={{width: 100}}>
                    <Select showSearch options={countryOptions} onChange={(e) => selectCountry(e)}/>
                </Form.Item>   
            }
            />
        </Form.Item>
    </Form>
    </>
  )
}

export default PhoneNumberInput

然后只需在要使用的地方导入此组件并调用它:&lt; phonenumberInput/&gt;

ps ps这是指向 npm模块使用他们使用的标志,但是您可以检查他们的git 仅获取SVG文件,并将其用作图像在您的项目

更新
以前显示的示例,将电话号码存储为对象。仅保存一个字符串并与ANTD表格保持可行,您可能会对以下示例感兴趣。

此组件允许您简单地将其作为其他Antd的用户输入以Form.item的方式使用

import React, {useEffect, useState} from 'react'
import { Input, Select } from 'antd'
import {
  parsePhoneNumber,
  getCountries,
  formatPhoneNumberIntl,
} from 'react-phone-number-input'
import { CountryCode } from 'libphonenumber-js'
import { useTranslation } from 'react-i18next'

type PhoneNumberFormItemProps = {
  value?: any
  onChange?: (value: any) => void
}

export const PhoneNumberInput: React.FC<PhoneNumberFormItemProps> = ({value, onChange}) => {
  const { t } = useTranslation('single_components')
  const [countryCode, setCountryCode] = useState<CountryCode>()

  useEffect(() => {
    if(value){
      let parsed = parsePhoneNumber(value)
      setCountryCode(parsed?.country)
    }
  }, [value])

  const countryOptions: { value: string; label: React.ReactElement }[] =
    getCountries().map((code) => {
      return {
        value: code,
        label: (
          <span className="phone-icon-container">
            {' '}
            <img src={`images/flags/${code}.svg`} className="country-flag-icon" /> {code}
          </span>
        ),
      }
  })

  function numberInputChanged(phone: string) {
    let parsed = parsePhoneNumber(phone, countryCode)
    //setCountryCode(parsed?.country) //useless if there is useEffect 
    if(typeof onChange === 'function') onChange(parsed ? formatPhoneNumberIntl(parsed.number) : phone)
  }

  function selectCountry(code: any) {
    setCountryCode(code)
    let parsed = parsePhoneNumber(value, code)
    if(typeof onChange === 'function') onChange(parsed && formatPhoneNumberIntl(parsed.number))
  }

  return (
    <Input
      className="phone-input-container"
      placeholder={t('User\'s phone number')}
      onChange={(e) => numberInputChanged(e.target.value)}
      value={value}
      addonBefore={
        <Select
          showSearch
          options={countryOptions}
          onSelect={selectCountry}
          // value={parsePhoneNumber(value)?.country} //if value === null => crash 
          value={countryCode}
          placeholder={
            <img src="images/flags/unknown.svg" className="unknown-country-flag-icon" />
          }
          className="same-as-input phone-country-select"
        />
      }
    />
  )
}

export default PhoneNumberInput

To make it workable with antd I can suggest you to make your own component inside of your react app intead of using their. Let's call this component PhoneNumberInput.tsx.
You can use their functions to format and parse phone number input such as formatPhoneNumber, parsePhoneNumber, getCountries, however take a look to the dependencies they use in their npm module. From my point of view it would be better to use directly libphonenumber-js.

import React from 'react'
import { Form, Button, Input, Select } from 'antd'

import { formatPhoneNumber, parsePhoneNumber, getCountries } from 'react-phone-number-input'
import { CountryCode } from 'libphonenumber-js/types'

export const PhoneNumberInput = () => {
  const [form] = Form.useForm()


  const countryOptions: { value: string, label: JSX.Element}[] = getCountries().map((ZZ) => {
    return {
        value: ZZ,
        label: <span> <img src={`images/flags/${ZZ}.svg`} className='country-flag-icon'/> {ZZ}</span> ,
    }
  })

  countryOptions.unshift({
    value: 'unknown',
    label: <span><img src='images/flags/unknown.svg'  className='country-flag-icon'/></span>,
  })


  function numberInputChanged(phone: any){
    const parsed = parsePhoneNumber(phone, form.getFieldValue('countryCode')) 
    form.setFieldsValue({
      phoneNumber: parsed && formatPhoneNumber(parsed.number)||phone,
      countryCode: (parsed && parsed.country) || 'unknown',
    })
  }


  function selectCountry(ZZ: any){
    form.setFieldValue('countyCode', ZZ)
    const phoneNumber = form.getFieldValue('phoneNumber')
    if(!phoneNumber) return
    const parsed = parsePhoneNumber(phoneNumber, ZZ)
    parsed && parsed.number && form.setFieldValue('phoneNumber', parsed && formatPhoneNumber(parsed.number))
    
  }

  function getPhoneNumber(phone: {countryCode: CountryCode | 'unknown', phoneNumber: string}){
    if(phone.countryCode === 'unknown') console.log('not formatted',phone.phoneNumber)
    else console.log('formatted:', parsePhoneNumber(phone.phoneNumber, phone.countryCode)?.number)
  }

  return (
    <>
    <Button
        form="test-phone-form"
        htmlType="submit"
    >
        finish
    </Button>
    <Form 
        id="test-phone-form"
        form={form}
        onFinish={getPhoneNumber}
    >
        <Form.Item name={"phoneNumber"} >
            <Input
            placeholder="Your phone number"
            onChange={(e) => numberInputChanged(e.target.value)}
            addonBefore={
                <Form.Item name={"countryCode"} style={{width: 100}}>
                    <Select showSearch options={countryOptions} onChange={(e) => selectCountry(e)}/>
                </Form.Item>   
            }
            />
        </Form.Item>
    </Form>
    </>
  )
}

export default PhoneNumberInput

Then just import this component wherever you want to use it and call it: <PhoneNumberInput/>

P.S. here is a link to npm module with flags they used, however you might check their git to get only svg files and use them as images in your project

UPDATE
example showed before, stores phone number as an object. To save only a string and keep it workable with antd forms you might be interested in a following example.

This component allows you to use it so simply as others antd's user inputs in Form.Item

import React, {useEffect, useState} from 'react'
import { Input, Select } from 'antd'
import {
  parsePhoneNumber,
  getCountries,
  formatPhoneNumberIntl,
} from 'react-phone-number-input'
import { CountryCode } from 'libphonenumber-js'
import { useTranslation } from 'react-i18next'

type PhoneNumberFormItemProps = {
  value?: any
  onChange?: (value: any) => void
}

export const PhoneNumberInput: React.FC<PhoneNumberFormItemProps> = ({value, onChange}) => {
  const { t } = useTranslation('single_components')
  const [countryCode, setCountryCode] = useState<CountryCode>()

  useEffect(() => {
    if(value){
      let parsed = parsePhoneNumber(value)
      setCountryCode(parsed?.country)
    }
  }, [value])

  const countryOptions: { value: string; label: React.ReactElement }[] =
    getCountries().map((code) => {
      return {
        value: code,
        label: (
          <span className="phone-icon-container">
            {' '}
            <img src={`images/flags/${code}.svg`} className="country-flag-icon" /> {code}
          </span>
        ),
      }
  })

  function numberInputChanged(phone: string) {
    let parsed = parsePhoneNumber(phone, countryCode)
    //setCountryCode(parsed?.country) //useless if there is useEffect 
    if(typeof onChange === 'function') onChange(parsed ? formatPhoneNumberIntl(parsed.number) : phone)
  }

  function selectCountry(code: any) {
    setCountryCode(code)
    let parsed = parsePhoneNumber(value, code)
    if(typeof onChange === 'function') onChange(parsed && formatPhoneNumberIntl(parsed.number))
  }

  return (
    <Input
      className="phone-input-container"
      placeholder={t('User\'s phone number')}
      onChange={(e) => numberInputChanged(e.target.value)}
      value={value}
      addonBefore={
        <Select
          showSearch
          options={countryOptions}
          onSelect={selectCountry}
          // value={parsePhoneNumber(value)?.country} //if value === null => crash 
          value={countryCode}
          placeholder={
            <img src="images/flags/unknown.svg" className="unknown-country-flag-icon" />
          }
          className="same-as-input phone-country-select"
        />
      }
    />
  )
}

export default PhoneNumberInput
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文