I've created my own AutoComplete form element based on @nextui-org/autocomplete to be able to use it along with react-hook-form. I am relatively new at TS, at least on generics, so I am not sure if I am overusing generics or have any mistakes really.
// AutoComplete.tsx
import {
Autocomplete as NUIAutocomplete,
AutocompleteItem as NUIAutocompleteItem,
AutocompleteProps as NUIAutocompleteProps,
} from '@nextui-org/autocomplete';
import { CollectionChildren } from '@react-types/shared';
import React from 'react';
import { Control, FieldValues, Path, useController } from 'react-hook-form';
interface IAutoCompleteProps<T extends FieldValues, K extends object>
extends Omit<NUIAutocompleteProps, 'children' | 'defaultItems'> {
name: Path<T>;
control: Control<T>;
defaultItems?: Iterable<K>;
children: CollectionChildren<K>;
}
const AutoComplete = <T extends FieldValues, K extends object>({
name,
control,
variant = 'underlined',
...otherProps
}: IAutoCompleteProps<T, K>) => {
const controller = useController({ name, control });
return (
<NUIAutocomplete
aria-label={name}
onSelectionChange={(value) => {
console.log('value', value);
controller.field.onChange(value);
}}
onClear={() => controller.field.onChange('')}
{...controller.field}
variant={variant}
{...otherProps}
>
{otherProps.children as CollectionChildren<object>}
</NUIAutocomplete>
);
};
export default AutoComplete;
export const AutoCompleteItem = NUIAutocompleteItem;
And this is how I use it:
// EditClassroom.jsx
'use client';
import { Classroom, Grade } from '@prisma/client';
import React, { useEffect } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { trpc } from '@/app/_trpc/client';
import { translateErrors } from '@/lib/translations';
import AutoComplete, {
AutoCompleteItem,
} from '@/components/ui/form/AutoComplete';
interface IEditClassroomProps {
onFinish: () => void;
classroom?: Classroom;
}
const EditClassroom = ({ onFinish, classroom }: IEditClassroomProps) => {
// ...
const { data: grades } = trpc.grade.list.useQuery({});
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
reset,
watch,
setValue,
control,
} = useForm<Classroom>();
// ...
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/*...*/}
<AutoComplete
name={'gradeId'}
defaultItems={grades.results}
isRequired={true}
selectedKey={watch('gradeId')}
label={t('recordFields.grade')}
errorMessage={translateErrors(tErrors, 'gradeId', errors.gradeId)}
control={control}
>
{(grade) => {
return (
<AutoCompleteItem key={grade.id} textValue={grade.name}>
{grade.name}
</AutoCompleteItem>
);
}}
</AutoComplete>
{/*...*/}
</form>
);
};
export default EditClassroom;