Skip to content

Instantly share code, notes, and snippets.

@aaronksaunders
Created March 11, 2020 21:15
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save aaronksaunders/86251779cbefacf4c9397ea6a574809a to your computer and use it in GitHub Desktop.
Save aaronksaunders/86251779cbefacf4c9397ea6a574809a to your computer and use it in GitHub Desktop.
Typscript version of react-hook-form code with Ionic Framework
import React, { useState } from "react";
import {
IonApp,
IonPage,
IonHeader,
IonContent,
IonItem,
IonLabel,
IonInput,
IonRadioGroup,
IonListHeader,
IonRadio,
IonSelect,
IonSelectOption,
IonRange,
IonButton
} from "@ionic/react";
import { useForm, Controller } from "react-hook-form";
/* Core CSS required for Ionic components to work properly */
import "@ionic/react/css/core.css";
/* Basic CSS for apps built with Ionic */
import "@ionic/react/css/normalize.css";
import "@ionic/react/css/structure.css";
import "@ionic/react/css/typography.css";
/* Optional CSS utils that can be commented out */
import "@ionic/react/css/padding.css";
import "@ionic/react/css/float-elements.css";
import "@ionic/react/css/text-alignment.css";
import "@ionic/react/css/text-transformation.css";
import "@ionic/react/css/flex-utils.css";
import "@ionic/react/css/display.css";
/* Theme variables */
import "./theme/variables.css";
let renderCount = 0;
let initialValues = {
rangeInfo: -100,
fullName: "",
gender: "",
techCos: "",
email: ""
};
const App: React.FC = () => {
const { control, handleSubmit, formState, reset, errors } = useForm({
defaultValues: { ...initialValues },
mode: "onChange"
});
const [data, setData] = useState();
renderCount++;
/**
*
* @param _fieldName
*/
const showError = (_fieldName: string) => {
let error = (errors as any)[_fieldName];
return error ? (
<div style={{ color: "red", fontWeight: "bold" }}>
{error.message || "Field Is Required"}
</div>
) : null;
};
/**
*
* @param data
*/
const onSubmit = (data: any) => {
alert(JSON.stringify(data, null, 2));
setData(data);
};
return (
<IonApp>
<IonPage>
<IonHeader>
<h2>React Hook Form work with Ionic Components</h2>
</IonHeader>
<IonContent>
<form onSubmit={handleSubmit(onSubmit)} style={{ padding: 18 }}>
<span className="counter">Render Count: {renderCount}</span>
<IonItem>
<IonLabel>Name - IonInput</IonLabel>
<Controller
as={IonInput}
control={control}
onChangeName="onIonChange"
onChange={([selected]) => {
console.log("fullName", selected.detail.value);
return selected.detail.value;
}}
name="fullName"
rules={{
required: true,
minLength: { value: 4, message: "Must be 4 chars long" }
}}
/>
</IonItem>
{showError("fullName")}
<IonItem>
<IonLabel>Email</IonLabel>
<Controller
as={IonInput}
control={control}
onChangeName="onIonChange"
onChange={([selected]) => {
return selected.detail.value;
}}
name="email"
rules={{
required: true,
pattern: {
value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "invalid email address"
}
}}
/>
</IonItem>
{showError("email")}
<Controller
as={
<IonRadioGroup>
<IonListHeader>
<IonLabel>
<h1>Manufacturers</h1>
</IonLabel>
</IonListHeader>
<IonItem>
<IonLabel>Apple</IonLabel>
<IonRadio value="apple" />
</IonItem>
<IonItem>
<IonLabel>Amazon</IonLabel>
<IonRadio value="amazon" />
</IonItem>
<IonItem>
<IonLabel>Microsoft</IonLabel>
<IonRadio value="microsoft" />
</IonItem>
</IonRadioGroup>
}
control={control}
name="techCos"
rules={{ required: true }}
onChangeName="onIonChange"
onChange={([selected]) => {
console.log(selected.detail.value);
return selected.detail.value;
}}
/>
<IonItem>
<IonLabel>Gender</IonLabel>
<Controller
as={
<IonSelect placeholder="Select One">
<IonSelectOption value="FEMALE">Female</IonSelectOption>
<IonSelectOption value="MALE">Male</IonSelectOption>
</IonSelect>
}
control={control}
onChangeName="onIonChange"
onChange={([selected]) => {
console.log(selected.detail.value);
return selected.detail.value;
}}
name="gender"
rules={{ required: true }}
/>
</IonItem>
<IonItem>
<Controller
as={
<IonRange min={-200} max={200} color="secondary">
<IonLabel slot="start">-200</IonLabel>
<IonLabel slot="end">200</IonLabel>
</IonRange>
}
control={control}
name="rangeInfo"
onChangeName="onIonChange"
onChange={([selected]) => {
console.log(selected.detail.value);
return selected.detail.value;
}}
rules={{ required: true }}
/>
</IonItem>
<IonItem>
<IonLabel>
formState.isValid: {(formState.isValid === true).toString()}
</IonLabel>
</IonItem>
{data && (
<pre style={{ textAlign: "left" }}>
{JSON.stringify(data, null, 2)}
</pre>
)}
<IonButton
type="button"
onClick={() => {
reset(initialValues);
}}
>
Reset Form
</IonButton>
<IonButton type="submit" disabled={formState.isValid === false}>
submit
</IonButton>
</form>
</IonContent>
</IonPage>
</IonApp>
);
};
export default App;
@tx-tim
Copy link

tx-tim commented Jul 25, 2020

Thanks for putting this tutorial together - it was super helpful! FYI - rhf removed the onChangeName prop (among others) beginning with version 6.
Looks like the recommended way forward is to use a renderprop in place of component

See changelog and example here:
https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md#changed-2

@aaronksaunders
Copy link
Author

aaronksaunders commented Jul 25, 2020 via email

@realadamsmith
Copy link

Hey Aaron just wondering how to enable the submit button, its not enabling when I fill out all the fields lol

@ptmkenny
Copy link

Could you please consider adding an example for IonCheckbox? I'm stuck because somehow boolean values seem to get treated differently.

@mikeb26
Copy link

mikeb26 commented Oct 3, 2022

Oct 2022: is there a more recent version that works? I'm currently getting:

Failed to compile.

TS2322: Type '{ as: ForwardRefExoticComponent<IonInput & Pick<HTMLAttributes<HTMLIonInputElement>, "onBlur" | "onChange" | "onSubmit" | "hidden" | "dir" | "slot" | ... 246 more ... | "onTransitionEndCapture"> & StyleReactProps & RefAttributes<...>>; ... 4 more ...; rules: { ...; }; }' is not assignable to type 'IntrinsicAttributes & { render: ({ field, fieldState, formState, }: { field: ControllerRenderProps<{ rangeInfo: number; fullName: string; gender: string; techCos: string; email: string; }, "fullName">; fieldState: ControllerFieldState; formState: UseFormStateReturn<...>; }) => ReactElement<...>; } & UseControllerPro...'.
  Property 'as' does not exist on type 'IntrinsicAttributes & { render: ({ field, fieldState, formState, }: { field: ControllerRenderProps<{ rangeInfo: number; fullName: string; gender: string; techCos: string; email: string; }, "fullName">; fieldState: ControllerFieldState; formState: UseFormStateReturn<...>; }) => ReactElement<...>; } & UseControllerPro...'.
    79 |               <IonLabel>Name - IonInput</IonLabel>
    80 |               <Controller
  > 81 |                 as={IonInput}
       |                 ^^
    82 |                 control={control}
    83 |                 onChangeName="onIonChange"
    84 |                 onChange={([selected]) => {


[ERROR] An error occurred while running subprocess react-scripts.
        
        react-scripts build exited with exit code 1.
        
        Re-running this command with the --verbose flag may provide more information.
make: *** [Makefile:4: build] Error 1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment