为什么 React Hook Form 认为字段未填充 Chakra UI?
Why does React Hook Form think field not filled in with Chakra UI?
我使用 Chakra UI 在 React 中构建了一个自动完成输入的样式。我还使用 react-hook-form 来处理表单提交钩子。
当我使用自动完成功能填写表单时,我可以成功地从下拉列表中 select 选择一个选项。然而,当提交表单时,React-Hook-Form 似乎认为我没有填写字段,因为没有提交任何内容并且没有调用 onSubmit 调用。
我使用 forwardRef 将 ref 传递给输入,这应该是注册这些字段所需的全部。
这里是一个沙箱,可以使调试更容易 - 注意没有从提交调用的日志,也没有来自 RHF 的任何错误 - 两者之一应该出现 - https://codesandbox.io/s/chakra-hook-fail-49ppb
表单代码为:
const test: AutoCompleteSuggestions[] = [
{key: "US", value: "USA"},
{key: "UK", value: "United Kingdom"},
]
function onSubmit(values: QueryFormParams) {
console.log('V'); //Never gets called
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl isInvalid={errors.name}>
<AutoComplete
{...register('selectedDeparture', {
required: 'Select a departure',
})}
placeholder={'Departure country...'}
isValid={v => setIsItemSelected(v)}
suggestions={test}
/>
</FormControl>
</form>
)
自动完成代码如下:
import React, {Ref, useState} from 'react';
import {Input, Table, Tbody, Td, Tr, VStack} from '@chakra-ui/react';
type AutoCompleteProps = {
suggestions: AutoCompleteSuggestions[]
isValid: (b: boolean) => void
placeholder: string
}
export type AutoCompleteSuggestions = {
key: string,
value: string
}
// eslint-disable-next-line react/display-name
export const AutoComplete = React.forwardRef(({suggestions, isValid, placeholder}: AutoCompleteProps, ref: Ref<HTMLInputElement>) => {
const [filteredSuggestions, setFilteredSuggestions] = useState<AutoCompleteSuggestions[]>();
const [showSuggestions, setShowSuggestions] = useState<boolean>(false);
const [userInput, setUserInput] = useState<string>('');
function onChange(e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) {
console.log('Changing', userInput, e.currentTarget.value);
const newUserInput = e.currentTarget.value;
if (!newUserInput) {
//setActiveSuggestion(-1);
setFilteredSuggestions([]);
setShowSuggestions(false);
isValid(true);
setUserInput(e.currentTarget.value);
}
const filteredSuggestions = suggestions.filter(suggestion => suggestion.value.toLowerCase().startsWith(userInput.toLowerCase()));
//setActiveSuggestion(e.target.innerText);
setFilteredSuggestions(filteredSuggestions);
setShowSuggestions(true);
isValid(false);
setUserInput(e.currentTarget.value);
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
function onClick(e: MouseEvent<HTMLLIElement, MouseEvent>) {
console.log('Clicked', e.target.innerText);
setFilteredSuggestions([]);
setShowSuggestions(false);
isValid(true);
setUserInput(e.target.innerText);
}
let suggestionsListComponent;
if (showSuggestions && userInput) {
if (filteredSuggestions?.length) {
suggestionsListComponent = (
<Table className={'suggestions'} position={'absolute'} top={10} left={0} right={0} variant='simple' zIndex={999}>
<Tbody>
{filteredSuggestions?.map((suggestion, index) => {
return (
<Tr key={index}
_hover={{
background: 'gray.200',
color: 'green',
}}
onClick={onClick}>
<Td>
<span className={'selectedText'}>{suggestion.value}</span>
</Td>
</Tr>
);
})}
</Tbody>
</Table>
);
} else {
suggestionsListComponent = (
<div className="no-suggestions">
<em>No countries found for that input!</em>
</div>
);
}
}
return (
<>
<VStack position={'relative'}>
<Input
ref={ref}
type="text"
onChange={onChange}
value={userInput}
placeholder={placeholder}
/>
{suggestionsListComponent}
</VStack>
</>
);
});
export default AutoComplete;
我通过添加控制器解决了这个问题
<Controller
control={control}
name="selectedDeparture"
rules={{required: true}}
render={({field: {onChange, value}}) => (
<AutoComplete
onChange={(event, item) => {
onChange(item);
}}
placeholder={'Departure country...'}
isValid={v => setIsItemSelected(v)}
suggestions={countriesMap}
/>
)}
/>
我使用 Chakra UI 在 React 中构建了一个自动完成输入的样式。我还使用 react-hook-form 来处理表单提交钩子。
当我使用自动完成功能填写表单时,我可以成功地从下拉列表中 select 选择一个选项。然而,当提交表单时,React-Hook-Form 似乎认为我没有填写字段,因为没有提交任何内容并且没有调用 onSubmit 调用。
我使用 forwardRef 将 ref 传递给输入,这应该是注册这些字段所需的全部。
这里是一个沙箱,可以使调试更容易 - 注意没有从提交调用的日志,也没有来自 RHF 的任何错误 - 两者之一应该出现 - https://codesandbox.io/s/chakra-hook-fail-49ppb
表单代码为:
const test: AutoCompleteSuggestions[] = [
{key: "US", value: "USA"},
{key: "UK", value: "United Kingdom"},
]
function onSubmit(values: QueryFormParams) {
console.log('V'); //Never gets called
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl isInvalid={errors.name}>
<AutoComplete
{...register('selectedDeparture', {
required: 'Select a departure',
})}
placeholder={'Departure country...'}
isValid={v => setIsItemSelected(v)}
suggestions={test}
/>
</FormControl>
</form>
)
自动完成代码如下:
import React, {Ref, useState} from 'react';
import {Input, Table, Tbody, Td, Tr, VStack} from '@chakra-ui/react';
type AutoCompleteProps = {
suggestions: AutoCompleteSuggestions[]
isValid: (b: boolean) => void
placeholder: string
}
export type AutoCompleteSuggestions = {
key: string,
value: string
}
// eslint-disable-next-line react/display-name
export const AutoComplete = React.forwardRef(({suggestions, isValid, placeholder}: AutoCompleteProps, ref: Ref<HTMLInputElement>) => {
const [filteredSuggestions, setFilteredSuggestions] = useState<AutoCompleteSuggestions[]>();
const [showSuggestions, setShowSuggestions] = useState<boolean>(false);
const [userInput, setUserInput] = useState<string>('');
function onChange(e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) {
console.log('Changing', userInput, e.currentTarget.value);
const newUserInput = e.currentTarget.value;
if (!newUserInput) {
//setActiveSuggestion(-1);
setFilteredSuggestions([]);
setShowSuggestions(false);
isValid(true);
setUserInput(e.currentTarget.value);
}
const filteredSuggestions = suggestions.filter(suggestion => suggestion.value.toLowerCase().startsWith(userInput.toLowerCase()));
//setActiveSuggestion(e.target.innerText);
setFilteredSuggestions(filteredSuggestions);
setShowSuggestions(true);
isValid(false);
setUserInput(e.currentTarget.value);
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
function onClick(e: MouseEvent<HTMLLIElement, MouseEvent>) {
console.log('Clicked', e.target.innerText);
setFilteredSuggestions([]);
setShowSuggestions(false);
isValid(true);
setUserInput(e.target.innerText);
}
let suggestionsListComponent;
if (showSuggestions && userInput) {
if (filteredSuggestions?.length) {
suggestionsListComponent = (
<Table className={'suggestions'} position={'absolute'} top={10} left={0} right={0} variant='simple' zIndex={999}>
<Tbody>
{filteredSuggestions?.map((suggestion, index) => {
return (
<Tr key={index}
_hover={{
background: 'gray.200',
color: 'green',
}}
onClick={onClick}>
<Td>
<span className={'selectedText'}>{suggestion.value}</span>
</Td>
</Tr>
);
})}
</Tbody>
</Table>
);
} else {
suggestionsListComponent = (
<div className="no-suggestions">
<em>No countries found for that input!</em>
</div>
);
}
}
return (
<>
<VStack position={'relative'}>
<Input
ref={ref}
type="text"
onChange={onChange}
value={userInput}
placeholder={placeholder}
/>
{suggestionsListComponent}
</VStack>
</>
);
});
export default AutoComplete;
我通过添加控制器解决了这个问题
<Controller
control={control}
name="selectedDeparture"
rules={{required: true}}
render={({field: {onChange, value}}) => (
<AutoComplete
onChange={(event, item) => {
onChange(item);
}}
placeholder={'Departure country...'}
isValid={v => setIsItemSelected(v)}
suggestions={countriesMap}
/>
)}
/>