I'm currently trying to create a dynamic select/input component where you can choose values from select options or type your own value inside an input field by selecting the "other" select option.
Right now I get stuck by updating the form data equally to the value of the selected option / input value. The Form Data Value always persist on the initial / default value.
App.js
...
export default function App() {
const methods = useForm({});
const { handleSubmit } = methods;
const customSalutationOptions = [
{ title: "Not specified", value: "null" },
{ title: "Male", value: "male" },
{ title: "Female", value: "female" }
];
const defaultValues = {
salutation: "null"
};
const onSubmit = (data) => {
console.log(data);
};
return (
<div className="App">
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<SelectOrInput
variant="outlined"
name={`contactPerson[0].salutation`}
defaultValue={defaultValues}
selectOptions={customSalutationOptions}
/>
<Button type="submit" color="primary" fullWidth variant="contained">
Submit
</Button>
</form>
</FormProvider>
</div>
);
}
components/SelectOrInput.tsx
...
type Props = {
name: string;
label: string;
selectOptions: [{ title: string; value: string }];
defaultValue: any;
shouldUnregister: boolean;
variant: "filled" | "outlined" | "standard";
};
export default function SelectOrInput({
name,
label,
selectOptions,
defaultValue,
shouldUnregister,
variant
}: Props) {
const classes = useStyles();
const { control } = useFormContext();
const [showCustomInput, setShowCustomInput] = useState(false);
const [value, setValue] = useState(selectOptions[0].value);
const additionalInput = [{ title: "Other", value: "" }];
const combindedOptions = selectOptions.concat(additionalInput);
const handleInputSelectChange = (
event: React.ChangeEvent<{ value: unknown }>
): void => {
const value = event.target.value as string;
if (value === "") {
const newState = !showCustomInput;
setShowCustomInput(newState);
console.log(value);
setValue(value);
} else {
setValue(value);
}
};
const resetCustomInputToSelect = (event: React.MouseEvent<HTMLElement>) => {
const newState = !showCustomInput;
setValue(combindedOptions[0].value);
setShowCustomInput(newState);
};
return (
<>
{showCustomInput ? (
<FormControl className={classes.input}>
<Controller
name={name}
control={control}
shouldUnregister={shouldUnregister}
render={({ field }) => (
<TextField
{...field}
label={label}
InputLabelProps={{ shrink: true }}
variant={variant}
placeholder="Other..."
autoFocus
type="text"
onChange={handleInputSelectChange}
value={value}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
size="small"
onClick={resetCustomInputToSelect}
id="custominput-closebutton"
>
<CloseIcon fontSize="small" />
</IconButton>
</InputAdornment>
)
}}
></TextField>
)}
/>
</FormControl>
) : (
<FormControl className={classes.input} variant={variant}>
<InputLabel id={`label-select-${label}`}>{label}</InputLabel>
<Controller
name={name}
defaultValue={defaultValue}
control={control}
shouldUnregister={shouldUnregister}
render={({ field }) => (
<Select
{...field}
label={label}
labelId={`label-select-${label}`}
value={value}
MenuProps={{
anchorOrigin: {
vertical: "bottom",
horizontal: "left"
},
getContentAnchorEl: null
}}
onChange={handleInputSelectChange}
>
{combindedOptions.map((option, index) => (
<MenuItem key={option.title} value={`${option.value}`}>
{option.title}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
)}
</>
);
}
...
To give a better example I provided a CSB: