Using React Hook Form with Ionic React Components
Tags: react, javascript, ionic, webdev
Published: 2020-03-11 02:14:25


> **see updated post example for using v6+** - https://dev.to/aaronksaunders/using-react-hook-form-with-ionic-react-components-update-1463

Setting up [react-hook-form](https://react-hook-form.com/) is pretty straight forward; You get started by importing the library and defining and initializing the custom hook with any default values.

>Not going to cover too much of the basics since there is extensive documentation provided on the library's website: [Getting Started](https://react-hook-form.com/get-started)

```javascript
// the import
import { useForm, Controller } from "react-hook-form";

// set the default values for the controls
let initialValues = {
  rangeInfo: -100,
  fullName: "",
  gender: "",
  techCos: "",
  email: ""
};


const App () => {
  const { control, register, handleSubmit, errors, formState } = useForm({
    defaultValues: initialValues
  });
  return (<div></div>)
}
```
and then we have the `onSubmit` function that is called when the form is submitted we use this functaion as a way to the values from the form. Finally we also are managing the state locally using `useState`. We are storing the local state information in the variable `data`.
```javascript
// the import
import { useForm, Controller } from "react-hook-form";

const App () => {
  const { control, register, handleSubmit, errors, formState } = useForm({
    defaultValues: initialValues
  });

  const [data, setData] = useState();

  const onSubmit = data => {
    alert(JSON.stringify(data, null, 2));
    setData(data);
  };

  return (<div></div>)
}

```
Next we set up the form for use in the application; please note the use of the `onSubmit` function in the form

>I have excluded a lot of the Ionic components for setting up the page, the header and such but they are included in the project and sample code provided at the end of the post
```react
// the import
import { useForm, Controller } from "react-hook-form";


const App () => {
  const { control, register, handleSubmit, errors, formState } = useForm({
    defaultValues: initialValues
  });

  const [data, setData] = useState();

  const onSubmit = data => {
    alert(JSON.stringify(data, null, 2));
    setData(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} >
    {/* here is where the Ionic Components will go /*}
    </form>
  )
}

```
Most of the Ionic Framework components basic functionality will work fine, track the errors and provide the values without all of the additional `useState` boilerplate code you often see in react applications but to get the real benefit of validation and error checking you need to wrap the Ionic Components in the [`Controller` Component](https://react-hook-form.com/get-started#ControlledInput)

We will start out first with the basic use on the react-hook-form before we dive in to a control wrapped Ionic Component.
```react
<IonItem>
  <IonLabel>Gender</IonLabel>
  <IonSelect
    placeholder="Select One"
    name="gender"
    ref={register({ required: true })}
  >
    <IonSelectOption value="FEMALE">Female</IonSelectOption>
    <IonSelectOption value="MALE">Male</IonSelectOption>
  </IonSelect>
</IonItem>
{showError("gender")}
```
As you can see here the simple `IonInput`is handled out of the box
```react
<IonItem>
  <IonLabel>Name</IonLabel>
  <IonInput name="name" ref={register({ required: true })}></IonInput>
</IonItem>
{showError("name")}
```

I created a simple error handler function to display the error message from the react-hook-form hook. The library creates an object as part of the hook that holds the errors that are generated when the form is validated.
```javascript
const showError = (_fieldName: string) => {
  {
    return (
      (errors as any)[_fieldName] && (
        <div
          style={{
            color: "red",
            padding: 5,
            paddingLeft: 12,
            fontSize: "smaller"
          }}
        >
          This field is required
        </div>
      )
    );
  }
};
```
### Using The React-Hook-Form Control Component
An example of where you have to use the [`Controller` Component](https://react-hook-form.com/get-started#ControlledInput) is with the `IonRange` Component

Using the `IonRange` Component requires the use of the react-hook-form `controller` property and listening for the `onIonChange` event to get the appropriate value from the `IonRange` Component.

We get the value from the IonRange component using the `selected.detail.value` property and set the object appropriately and let the react-hook-form hook handle it from there.
```react
<IonItem>
  <Controller
    as={
      <IonRange min={-200} max={200} color="secondary" >
        <IonLabel slot="start">-200</IonLabel>
        <IonLabel slot="end">200</IonLabel>
      </IonRange>
    }
    control={control}
    onChangeName="onIonChange"
    onChange={([selected]: any) => {
      return selected.detail.value;
    }}
    name="rangeInfo"
    rules={{ required: true }}
  />
</IonItem>
```
In the end to get the true value from the library and Ionic Framework's Web Components, I suggest you just wrap everything. I was picking and choosing specific components to wrap as needed and when I came to checking the form's state to see if the form was valid, or not I just went all in.

### Wrapping Everything in a Control
```react
<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")} {/* USING THE showError FUNCTION */}
```

A more complex control `IonRadioGroup` we cannot just wrap the component name like we did above since there are child components in play here. 
```react
<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;
  }}
/>
{/* we can get the error and potentially a custom message */}
{ errors.techCos && (errors.techCos.message || <span>Field Is Required</span>)}
```
### Error Checking and Form Validation
For verifying the contents of the form, you can get access to the `formState` object to determine of the form is valid. You can use it to keep the submit button disabled.
```html
<IonButton type="submit" disabled={formState.isValid === false}>
  submit
</IonButton>
```

If you are going to check for errors, you set the `mode` of when errors are check...
```javascript
const { control, register, handleSubmit, errors, formState } = useForm({
  defaultValues: initialValues,
  mode : 'onChange' // when the values change... check for errors
});
```
or we can check when fields are blurred, more information is available in the [react-form-hooks documentation](https://react-hook-form.com/get-started#Handleerrors).
```javascript
const { control, register, handleSubmit, errors, formState } = useForm({
  defaultValues: initialValues,
  mode : 'onBlur' // when the you blur... check for errors
});
```
### Source Code / Project / Video 
{% youtube 5MsXpmh3Un8 %}

[![Edit React Hook Form - Ionic Input Components](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/react-hook-form-ionic-input-components-29zur?fontsize=16&hidenavigation=1&theme=dark)

### Here is the Typescript Version of Code
{% gist https://gist.github.com/aaronksaunders/86251779cbefacf4c9397ea6a574809a file=App.tsx %}