我正在使用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与这个或任何其他电话号码库连接有什么想法吗?
评论中 @adsy 的提示解决了这个问题。
在组件中使用
useController
:(以及组件中
ref
的细微更改)。然后,当您调用它进行深度嵌套时,也解构
control
: