How to type 'option' in ControlledSelect's 'value' and 'onChange' when returning only selected value? #175
-
Regarding to @csandman response in #105 (comment)
How I can achieve the same behavior and satisfy a Typescript in the cleanest way? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 6 replies
-
First I'd recommend checking out the This example specifically has a single select made as a custom component for If there is anything about this example that doesn't fit your needs, let me know and I can try and make it more specific! EDIT: Ok based on your extra input I made a new example showing how you could do it with only the value. There are two main points to this change. The first is making sure the For the sake of this example, I also removed the Anyway here's the code of the new import React from "react";
import {
useController,
FieldValues,
UseControllerProps,
} from "react-hook-form";
import { FormErrorMessage, FormLabel, FormControl } from "@chakra-ui/react";
import {
Select,
Props as SelectProps,
GroupBase,
OptionBase,
} from "chakra-react-select";
interface SelectOption extends OptionBase {
label: string;
value: string;
}
interface ControlledSelectProps<
FormValues extends FieldValues = FieldValues,
Option = unknown,
Group extends GroupBase<Option> = GroupBase<Option>
>
extends Omit<SelectProps<Option, false, Group>, "name" | "defaultValue">,
UseControllerProps<FormValues> {
label?: string;
}
/**
* An attempt to make a reusable chakra-react-select form component
*
* @param props - The combined props of the chakra-react-select component and the useController hook
*/
function ControlledSelect<
FormValues extends FieldValues = FieldValues,
Option extends SelectOption = SelectOption,
Group extends GroupBase<Option> = GroupBase<Option>
>({
name,
label,
options,
control,
rules,
shouldUnregister,
...selectProps
}: ControlledSelectProps<FormValues, Option, Group>) {
const {
field: { value, onChange, ...fields },
fieldState: { error },
} = useController<FormValues>({
name,
control,
rules,
shouldUnregister,
});
const getFlatOptions = (): Option[] => {
if (!options?.length) {
return [];
}
return options.reduce<Option[]>((opts, optOrGroup) => {
if ("value" in optOrGroup) {
return [...opts, optOrGroup];
}
if ("options" in optOrGroup) {
return [...opts, ...optOrGroup.options];
}
return opts;
}, []);
};
const flatOptions = getFlatOptions();
return (
<FormControl label={label} isInvalid={!!error} id={name}>
{label && <FormLabel>{label}</FormLabel>}
<Select<Option, false, Group>
options={options}
value={
!!flatOptions.length && !!value
? flatOptions.find((option) => option.value === value)
: null
}
onChange={(optionOrOptions) => onChange(optionOrOptions?.value || null)}
{...selectProps}
{...fields}
/>
<FormErrorMessage>{error?.message}</FormErrorMessage>
</FormControl>
);
}
export default ControlledSelect; And here's a CodeSandbox for it: https://codesandbox.io/s/chakra-react-select-single-react-hook-form-with-zod-validation-typescript-only-value-single-wbedcw?file=/components/controlled-select.tsx |
Beta Was this translation helpful? Give feedback.
First I'd recommend checking out the
react-hook-form
section of the docs, as there are a bunch of examples there for multiple scenarios in JS and TS.This example specifically has a single select made as a custom component for
react-hook-form
in a TS app: https://codesandbox.io/s/chakra-react-select-single-react-hook-form-with-yup-validation-typescript-phmv0u?file=/components/controlled-select.tsxIf there is anything about this example that doesn't fit your needs, let me know and I can try and make it more specific!
EDIT:
Ok based on your extra input I made a new example showing how you could do it with only the value. There are two main points to this change. The first is making sure the