react-hook-form + chakra-ui + 任何电话号码库(可能是决斗参考问题)
P粉143640496
P粉143640496 2024-03-27 09:25:26
0
1
521

我正在使用react-hook-form来构建通用表单组件,这些组件通过useFormContext范例进行深度嵌套和引用,以实现组件的任意深度嵌套。我使用 Chakra-UI 进行造型。这一切工作正常。不过,我想在某些表单中添加国际电话号码输入。

我实际上并不关心我使用哪个库,只要它在 NextJS 上下文中具有高性能并且与 RHF 和 Chakra 一起工作即可,所以我愿意接受与下面完全不同的方向的建议。

我想我已经非常接近使用react-international-phone了。

我遇到的问题(我在其他库中也遇到过类似但略有不同的问题)是react-international-phone 与 Chakra 或react-hook-form 配合良好,但不能同时与两者配合使用同时。

在 Github 源代码中,react-international-phone 有一个与 Chakra-UI 集成的 Storybook 示例,其工作原理如下:

export const ChakraPhone = ({
  value,
  onChange,
}) => {
  const phoneInput = usePhoneInput({
    defaultCountry: 'us',
    value,
    onChange: (data) => {
      onChange(data.phone);
    },
  });

  return (
    <ChakraProvider>
        <Input
          value={phoneInput.phone}
          onChange={phoneInput.handlePhoneValueChange}
          ref={phoneInput.inputRef}
        />
    </ChakraProvider>
  );
};

如果我只是在react-hook-forms中使用Chakra Input组件,它看起来会像这样:

<ConnectForm>
    {({ formState: { errors }, register}) => (
        <form>
            <FormControl isInvalid={errors.phone}>
                <FormLabel htmlFor='phone'>Phone Number</FormLabel>
                <Input
                  id='phone'
                  name='phone'
                  {...register('phone')} />
            </FormControl>
        </form>
    )}
</ConnectForm>

将这两件事结合起来的两个问题是 ...register 返回 ref 到 html 输入,而 React-international-phone 需要将 onChange 作为属性传递给其 usePhoneInput 挂钩。

对于第一个问题,我想我可以使用这个答案并执行

<Input
    value={phoneInput.phone}
    onChange={phoneInput.handlePhoneValueChange}
    name={name}
    ref={(el) => {reactHookRef(el); phoneInput.inputRef(el)}}
/>

但是抱怨 phoneInput.inputRef 是一个对象而不是函数。事实上,文档说它是一个 React.RefObject<HTMLInputElement> ,这......我猜不是一个函数。但后来我不确定为什么 ref={phoneInput.inputRef} 在示例代码中起作用。

我认为我可以通过重构react-hook-form register响应并将返回的onChange传递给usePhoneInput钩子来解决第二个问题。

最初我尝试这样做

const PhoneNumberInput = (props) => {
    return (    
        <ConnectForm>
            {({ formState: { errors }, register }) => {
                const { onChange, onBlur, name, ref: reactHookRef } = register('phone');
                const phoneInput = usePhoneInput({
                    defaultCountry: 'gb',
                    onChange: onChange
                })

                return (    
                    <ConnectForm>
                        <Input
                            type='tel'
                            value={phoneInput.phone}
                            onChange={phoneInput.handlePhoneValueChange}
                            name={name}
                            ref={(el) => {reactHookRef(el); phoneInput.inputRef}}
                        />

但问题是 usePhoneInput 是一个钩子,所以实际上不能在那里调用它。我现在所在的位置是

const PhoneNumberInput = (props) => {
    const [ onChangeRHF, setOnChangeRHF ] = useState();

    const phoneInput = usePhoneInput({
        defaultCountry: 'gb',
        onChange: onChangeRHF
    })

    return (    
        <ConnectForm>
            {({ formState: { errors }, register }) => {
                const { onChange, onBlur, name, ref: reactHookRef } = register('phone');
                setOnChangeRHF(onChange);

                return (
                    <>
                        <InputGroup size={props?.size} id={props?.id || 'phone'}>
                            <InputLeftAddon width='4rem'>
                                <CountrySelector
                                    selectedCountry={phoneInput.country}
                                    onSelect={(country) => phoneInput.setCountry(country.iso2)}
                                    renderButtonWrapper={({ children, rootProps }) => 
                                        <Button {...rootProps} variant={'outline'} px={'4px'} mr={'8px'}>
                                            {children}
                                        </Button>
                                    }
                                />
                            </InputLeftAddon>
                        <Input
                            type='tel'
                            value={phoneInput.phone}
                            onChange={phoneInput.handlePhoneValueChange}
                            onBlur={onBlur}
                            name={name}
                            ref={(el) => {reactHookRef(el); phoneInput.inputRef}}
                        />

感觉已经很接近了,但它仍然不起作用。我已将其放入 CodeSandbox 中。 CodeSandbox 已损坏,在 App.js 中,我注释掉了对表单的调用,因为如果我取消注释,它会锁定我的浏览器:(

关于如何将react-hook-form和chakra-ui与这个或任何其他电话号码库连接有什么想法吗?

P粉143640496
P粉143640496

全部回复(1)
P粉680487967

评论中 @adsy 的提示解决了这个问题。

在组件中使用useController

const PhoneNumberInput = ({ control, name, size='md' }) => {
    const {
        field,
        fieldState: { invalid, isTouched, isDirty },
        formState: { touchedFields, dirtyFields }
    } = useController({
        name,
        control
    })

    const phoneInput = usePhoneInput({
        defaultCountry: 'gb',
        onChange: (data) => {
            field.onChange(data.phone);
        }
    })

    return (
        
            
                
                     phoneInput.setCountry(country.iso2)}
                        renderButtonWrapper={({ children, rootProps }) => 
                            
                        }
                    />
                
                 field.ref(el) && phoneInput.inputRef}
                />
            
        >
    )
};

export default PhoneNumberInput;

(以及组件中 ref 的细微更改)。

然后,当您调用它进行深度嵌套时,也解构 control


        {({ control, formState: { errors }, register}) => (
            
Phone Number {errors.phone && errors.phone.message}
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板