DEV Community

Cover image for Using React Hook Form with Ionic React Components
Aaron K Saunders for Ionic

Posted on • Edited on

Using React Hook Form with Ionic React Components

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 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

// 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>)
}
Enter fullscreen mode Exit fullscreen mode

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.

// 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>)
}

Enter fullscreen mode Exit fullscreen mode

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

// 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>
  )
}

Enter fullscreen mode Exit fullscreen mode

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

We will start out first with the basic use on the react-hook-form before we dive in to a control wrapped Ionic Component.

<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")}
Enter fullscreen mode Exit fullscreen mode

As you can see here the simple IonInputis handled out of the box

<IonItem>
  <IonLabel>Name</IonLabel>
  <IonInput name="name" ref={register({ required: true })}></IonInput>
</IonItem>
{showError("name")}
Enter fullscreen mode Exit fullscreen mode

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.

const showError = (_fieldName: string) => {
  {
    return (
      (errors as any)[_fieldName] && (
        <div
          style={{
            color: "red",
            padding: 5,
            paddingLeft: 12,
            fontSize: "smaller"
          }}
        >
          This field is required
        </div>
      )
    );
  }
};
Enter fullscreen mode Exit fullscreen mode

Using The React-Hook-Form Control Component

An example of where you have to use the Controller Component 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.

<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>
Enter fullscreen mode Exit fullscreen mode

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

<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 */}
Enter fullscreen mode Exit fullscreen mode

A more complex control IonRadioGroup we cannot just wrap the component name like we did above since there are child components in play here.

<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>)}
Enter fullscreen mode Exit fullscreen mode

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.

<IonButton type="submit" disabled={formState.isValid === false}>
  submit
</IonButton>
Enter fullscreen mode Exit fullscreen mode

If you are going to check for errors, you set the mode of when errors are check...

const { control, register, handleSubmit, errors, formState } = useForm({
  defaultValues: initialValues,
  mode : 'onChange' // when the values change... check for errors
});
Enter fullscreen mode Exit fullscreen mode

or we can check when fields are blurred, more information is available in the react-form-hooks documentation.

const { control, register, handleSubmit, errors, formState } = useForm({
  defaultValues: initialValues,
  mode : 'onBlur' // when the you blur... check for errors
});
Enter fullscreen mode Exit fullscreen mode

Source Code / Project / Video

Edit React Hook Form - Ionic Input Components

Here is the Typescript Version of Code

Top comments (18)

Collapse
 
aaronksaunders profile image
Aaron K Saunders

I noticed when I ported the code, that react-hook-form in codesandbox.io is older than the latest version. In the latest version, there is a breaking change.

Before:

import { TextInput } from 'react-native';

<Controller
  as={<TextInput style={{ borderWidth: 2, borderColor: 'black'}} />}
  name="text"
  control={args => ({
    value: args[0].nativeEvent.text,
  })}
  onChange={onChange}
/>

now:

import { TextInput } from 'react-native';

<Controller
  as={<TextInput style={{ borderWidth: 2, borderColor: 'black'}} />}
  name="text"
  control={args => args[0].nativeEvent.text}
  onChange={onChange}
/>

github.com/react-hook-form/react-h...

Collapse
 
sirajulm profile image
Sirajul Muneer • Edited

@aaronksaunders Thank you. This article was a life saver. Is there a way to handle IonCheckbox ? I tried the approach similar to Radio button but, doesn't work.

Collapse
 
aaronksaunders profile image
Aaron K Saunders

just seeing this, did you get it resolved yet?

Collapse
 
sirajulm profile image
Sirajul Muneer

Not yet. I'm currently working on something else. Will update you by the end of the day.

Collapse
 
cordial_40 profile image
cordial • Edited

Thanks for the post, very useful. If I actually want to submit the data to send to an email address, can I just use something like Formspree to do so and it will work in the native iOs and Android apps? Or would it be better to put it in something like firebase and send it from there?

Collapse
 
aaronksaunders profile image
Aaron K Saunders

you can use formspree, from what I have seen it should work

Collapse
 
cordial_40 profile image
cordial

great, thanks! Will give it a go. And Thanks for replying so quickly.

Collapse
 
bluebill1049 profile image
Bill

Thank you very much for writing up this awesome blog post.

Collapse
 
aaronksaunders profile image
Aaron K Saunders

your welcome, extremely helpful hook for efficiently managing forms in react... working on a new one showing hook use in modals and overmindjs for state management

Collapse
 
bluebill1049 profile image
Bill

looking forward for that!

Collapse
 
patelrikin profile image
Rikin Patel

This is great info, not many examples are out there. I posted comment on other areas where your content is displayed for other users, but this will break if you are using react-hook-form v6+.
In order to use the method from this post, you would have to use v5.7.2 or below

Collapse
 
aaronksaunders profile image
Aaron K Saunders • Edited

working on an updated post - dev.to/ionic/using-react-hook-form...

Collapse
 
aaronksaunders profile image
Aaron K Saunders

Added a gist with the typescript version of the code to the blog post

gist.github.com/aaronksaunders/862...

Collapse
 
adrianvarelag profile image
AdrianVarelaG

Really useful, you saved me.

Collapse
 
aaronksaunders profile image
Aaron K Saunders

glad it was helpful to you!

Collapse
 
solankey016 profile image
solankey016



formState.isValid: {(formState.isValid === true).toString()}


Why is it inside a label?

Collapse
 
aaronksaunders profile image
Aaron K Saunders

not sure I understand the question

Collapse
 
malkebulan profile image
Mensah Alkebu-Lan

Great article. You may want to do an article on Formtik as well.