为什么 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}
                                        />
                                    )}
                                />