I am trying to build my components over React-Hooks-Form library. I am using Ionic 5 with React for my project. I manage to solve all my components, beside the select component. It is rendered, it has items, I can select the item, but on submit I did not get back the selected value in the object. Here is what I tried:
export interface CompetitionReuqest {
address: string;
courseId: number;
date: Date;
departmentId: number;
description: string;
documentLink: string;
imageLink: string;
place: string;
title: string;
typeId: number;
isPayable: boolean;
isInternational: boolean;
isGroup: boolean;
}
export interface IHookFormSelectlistProps<T> extends IBaseProps
{
label: string;
value?: string;
data: T[];
placeholder?: string;
displayMember: (item: T) => string;
valueMember: (item: T) => string;
onChange?: (item: T) => void;
reference<TFieldElement extends FieldElement<T>>(rules?: RegisterOptions): (ref: (TFieldElement & Ref) | null) => void;
}
function HookFormSelectList<T>(props: IHookFormSelectlistProps<T>): JSX.Element
{
const [selectedValue, setSelectedValue] = useState<string>();
useEffect(() =>
{
if (props.value)
{
setSelectedValue(props.value);
}
}, []);
return (
<React.Fragment>
<IonItem>
{
props.label &&
<IonLabel position="floating" class="label">{props.label}</IonLabel>
}
<Controller render={ ({ value, onBlur, onChange }) =>
(
<IonSelect
aria-invalid={props.errors && props.errors[props.name] ? "true" : "false"}
aria-describedby={`${props.name}Error`}
onBlur={onBlur}
onIonChange={(e) =>
{
const item: T = props.data.toEnum()
.FirstOrDefault((x: T) => props.valueMember(x) === e.detail.value);
if (props.onChange && item)
{
setSelectedValue(props.valueMember(item));
props.onChange(item);
}
onChange();
}}
value={selectedValue}
defaultValue={selectedValue}
disabled={props.disabled ? props.disabled : false}
placeholder={props.placeholder ? props.placeholder : "Válasszon ..."}>
{props.data.map((item) =>
(
<IonSelectOption
key={props.valueMember(item)}
value={props.valueMember(item)}
class="ion-select">
{props.displayMember(item)}
</IonSelectOption>
))}
</IonSelect>
)}
defaultValue={setSelectedValue}
name={props.name}
control={props.control}
onChangeName="onIonChange" />
</IonItem>
{
props.errors && props.errors[props.name] &&
(
<IonText color="danger" className="ion-padding-start">
<small>
<span role="alert" id={`${props.name}Error`}>
{props.errors[props.name].message}
</span>
</small>
</IonText>
)
}
</React.Fragment>
);
}
export default HookFormSelectList;
export function TypeSelectList(selectProps: IHookFormSelectlistProps<TypeEntity>): JSX.Element
{
return HookFormSelectList<TypeEntity>(selectProps);
}
export function DepartmentSelectList(selectProps: IHookFormSelectlistProps<DepartmentEntity>): JSX.Element
{
return HookFormSelectList<DepartmentEntity>(selectProps);
}
export function CourseSelectList(selectProps: IHookFormSelectlistProps<CourseEntity>): JSX.Element
{
return HookFormSelectList<CourseEntity>(selectProps);
}
And how am using it. I need an onChange event, to be available outside too! My react-hook-form form implementations [not all the code, this is only the relevant part]:
const { control, handleSubmit, errors, register } = useForm<CompetitionReuqest>
({
mode: "onChange",
resolver: yupResolver(validationSchema)
});
const onSubmit = async (data: CompetitionReuqest): Promise<void> =>
{
console.log("data:");
console.log({...data});
}
return (
<React.Fragment>
<InformationToast enabled={showToast}
message="A versenyfelhívás feltöltése sikeres volt."
onClick={() => onToasInformationClick()}/>
<form onSubmit={handleSubmit((data: CompetitionReuqest) => onSubmit(data))}>
<IonGrid>
<IonRow>
<IonCol sizeXl="4" sizeLg="12" sizeMd="12" sizeSm="12" sizeXs="12">
<DepartmentSelectList
name="departmentId"
label="Iskola típus"
value={department?.name}
data={departments}
placeholder="Kérem válasszon"
displayMember={(item: DepartmentEntity) => item.name}
valueMember={(item: DepartmentEntity) => itemname}
onChange={(item: DepartmentEntity) => onDepartmentChanged(item)}
control={control}
errors={errors}
reference={register} />
</IonCol>
<IonCol sizeXl="4" sizeLg="12" sizeMd="12" sizeSm="12" sizeXs="12">
<CourseSelectList
name="courseId"
label="Tantárgy"
value={course?.name}
data={courses}
placeholder="Kérem válasszon"
displayMember={(item: CourseEntity) => item.name}
valueMember={(item: CourseEntity) => item.name}
onChange={(item: CourseEntity) => onCourseChanged(item)}
control={control}
errors={errors}
disabled={disableCourses}
reference={register} />
</IonCol>
<IonCol sizeXl="4" sizeLg="12" sizeMd="12" sizeSm="12" sizeXs="12">
<TypeSelectList
name="typeId"
label="Verseny típus"
value={type?.name}
data={types}
placeholder="Kérem válasszon"
displayMember={(item: TypeEntity) => item.name}
valueMember={(item: TypeEntity) => item.name}
onChange={(item: TypeEntity) => onTypeChanged(item)}
control={control}
errors={errors}
reference={register} />
</IonCol>
</IonRow>
<IonRow>
<IonCol>
<IonButton expand="block" type="submit" className="ion-margin-top"
disabled={formState.isValid === false}>
MENTÉS
</IonButton>
</IonCol>
</IonRow>
</IonGrid>
</form>
</React.Fragment>
);
I think what you're missing is this:
onIonChange = {
(e) => {
/********************* other code */
value = props.valueMember(item);
console.log("value from render before internal onChange:" + value);
onChange(e); //<============= pass the e input to the onChange callback
},
};