Forms in React: React Hook Forms with Material UI and YUP
Tags: react, materialui, javascript
Published: 2020-09-06 18:57:12

In React there are lots of ways to write a form, some use libraries like Formik, Redux Form or some prefer the plain way of doing it writing everything from scratch. The advantage of using a form library is a lot of common form functionality is taken care of like validations, getting the whole form data in a single object and writing less code(this point is debatable :D)
One such form library in react is **[React Hook Form](https://react-hook-form.com/)**

# Why I choose to use React Hook Form?
I have tried a couple of form libraries the most popular being **Formik**, but none of these is, as fast as React Hook Form. In my web apps, my form usually has around 60-70 fields, and for such a large amount of fields, no form library comes close to React Hook Form in terms of performance, not even Formik.

# GOAL
In this article, we will cover how to create reusable form components like TextField, Select of Material UI, MultiSelect of *react-select* with React Hook Form. We will be using Yup for form validations and how it integrates with React Hook Form.

At the end of the article, I will share a git hub repo where I have included all the form components of Material UI with React Hook Form that one can easily reference or integrate into their projects 

# Table of Content
This article will be a long one. So I have divided my article into few sections
* [Initial setup](#heading1)
* [Basic form element binding with React Hook Form](#heading2)
  1. [TextField](#sub1)
  2. [Select](#sub2)
  3. [Multi-Select with Autocomplete (React-Select)](#sub3)
* [Validation with Yup](#heading3)
* [Pre populating form field data](#heading4)
* [Github repo](#heading5)
* [References](#heading6)

# Initial setup <a name="heading1"></a>
We will use `create-react-app` for this article. Follow the below steps to setup the basics
```
npx create-react-app hook-form-mui
cd hook-form-mui
npm install @material-ui/core @material-ui/icons react-hook-form yup @hookform/resolvers react-select styled-components @material-ui/pickers @date-io/moment@1.x moment
```
Once all the packages are installed, run the app once.
```
npm start
```

You will see the below page
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/vqk333d0eqtjdeml7se0.PNG)

# Basic form element binding with React Hook Form <a name="heading2"></a>
## 1. Textfield <a name="sub1"></a>
Create a folder in the *src* named *controls*. Inside *controls* folder create a folder *input*. Inside *input* folder create a file *index.js* (*src -> controls -> input -> index.js*)

*index.js* will have below code
```js
import React from "react";
import { useFormContext, Controller } from "react-hook-form";
import TextField from "@material-ui/core/TextField";

function FormInput(props) {
  const { control } = useFormContext();
  const { name, label } = props;


  return (
    <Controller
      as={TextField}
      name={name}
      control={control}
      defaultValue=""
      label={label}
      fullWidth={true}
      {...props}
    />
  );
}

export default FormInput;

```
Lets deep dive into the above code. 
When using *React Hook Form*, two primary concepts need to be kept in mind,
1. We have to *register* each form field that we use. This helps in form submission and validation.
2. Each form field should have a unique name associated with it.

In the above code, we are using a wrapper component called `Controller` provided by `react-hook-form` to register our form fields (in this case) `TextField` component.

As you can see we can pass additional props of `TextField` component and other props directly to the `Controller` component
```js
 <Controller
      as={TextField}
      name={name}
      control={control}
      defaultValue=""
      label={label}
      fullWidth={true}
      InputLabelProps={{
        className: required ? "required-label" : "",
        required: required || false,
      }}
      error={isError}
      helperText={errorMessage}
      {...props}
    />
```
`control` object contains methods for registering a controlled component into React Hook Form. The `control` object needs to be pass as a prop to the `Controller` component. 
`control` object is declared as :
```
const { control } = useFormContext();
```

In `App.js`, we will have the following code:
```js
import React from "react";
import { useForm, FormProvider } from "react-hook-form";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";

import FormInput from "./controls/input";

function App(props) {
  const methods = useForm();
  const { handleSubmit } = methods;
  
  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <div style={{ padding: "10px" }}>
      <Button
        variant="contained"
        color="primary"
        onClick={handleSubmit(onSubmit)}
      >
        SUBMIT
      </Button>

      <div style={{ padding: "10px" }}>
        <FormProvider {...methods}> // pass all methods into the context
          <form>
            <Grid container spacing={2}>
              <Grid item xs={6}>
                <FormInput name="name" label="Name" />
              </Grid>
            </Grid>
          </form>
        </FormProvider>
      </div>
    </div>
  );
}

export default App;
```
Let's deep dive into the `App.js` code.
The most important function is `useForm()` which is a hook provided by `react-hook-form`. `useForm()` contains various methods which is required for form validation, submission and registration of the form fields.
```js
 const methods = useForm();
 const { handleSubmit } = methods;
```
As in the above code `useForm()` provides a `method` object which contains `handleSubmit` function which is used for form submission on button click. In this case `SUBMIT` button.

```js
 <FormProvider {...methods}> 
          <form>
            <Grid container spacing={2}>
              <Grid item xs={6}>
                <FormInput name="name" label="Name" />
              </Grid>
            </Grid>
          </form>
        </FormProvider>
```
In the above block of code, we declare a `FormProvider` component under which our form and its respective fields will be declared. Also, we need to pass all the functions and objects of `methods` to `FormProvider` component. This is required as we are using a deep nested structured of custom form fields and the `useFormContext()` used in the `FormInput` component need to consume the functions and objects of `methods`

For the `FormInput` component we just need to pass `name` and `label` props.
```js
<FormInput name="name" label="Name" />
```

If you run the app, you should see:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/yl4zkvpc63bl91cd6ua0.PNG)

Type any text in the *Name* field and click on *SUBMIT* button. In the dev-console check the output. The output will be an object with the field name and corresponding value.

Now let's move on to create other field components in a similar fashion.

## 2. Select <a name="sub2"></a>
Create a new folder name *styles* under src. Create a new file *index.js* under *styles* folder (*src -> styles -> index.js*)
*index.js* will have following code
```js
import styled from "styled-components";
import { InputLabel } from "@material-ui/core";

export const StyledInputLabel = styled(InputLabel)`
  && {
    .req-label {
      color: #f44336;
    }
  }
`;
```
I'm using `styled-components` for my styling. `StyledInputLabel` will be used below in the `FormSelect` component. The main purpose of the above styling will be used during validation.

Create a new folder name *select* under *controls*, inside *select* folder create a *index.js* file (*controls -> select -> index.js*).

*index.js* will have following code
```js
import React from "react";
import { useFormContext, Controller } from "react-hook-form";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import InputLabel from "@material-ui/core/InputLabel";

const MuiSelect = (props) => {
  const { label, name, options } = props;

  return (
    <FormControl fullWidth={true}>
      <InputLabel htmlFor={name}>{label}</InputLabel>
      <Select id={name} {...props}>
        <MenuItem value="">
          <em>None</em>
        </MenuItem>
        {options.map((item) => (
          <MenuItem key={item.id} value={item.id}>
            {item.label}
          </MenuItem>
        ))}
      </Select>
    </FormControl>
  );
};

function FormSelect(props) {
  const { control } = useFormContext();
  const { name, label } = props;
  return (
    <React.Fragment>
      <Controller
        as={MuiSelect}
        control={control}
        name={name}
        label={label}
        defaultValue=""
        {...props}
      />
    </React.Fragment>
  );
}

export default FormSelect;
```
Things to note in the above code
1. `MuiSelect` function is a component which contains our UI for the rendering of the *Select* field. There are three main props `name`, `label` and `options`. `options` is an array of objects that contains the data to be displayed in the dropdown.
2. `FormSelect` is similar to `FormInput` component where again we are using `useFormContext()` method, `Controller` component and `control` object.

Let's see how we consume `FormSelect` in the *App.js*. Below is the new code in *App.js*
```js
import React from "react";
import { useForm, FormProvider } from "react-hook-form";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";

import FormInput from "./controls/input";
import FormSelect from "./controls/select";

function App(props) {
  const methods = useForm();
  const { handleSubmit } = methods;

  const onSubmit = (data) => {
    console.log(data);
  };

  const numberData = [
    {
      id: "10",
      label: "Ten",
    },
    {
      id: "20",
      label: "Twenty",
    },
    {
      id: "30",
      label: "Thirty",
    },
  ];

  return (
    <div style={{ padding: "10px" }}>
      <Button
        variant="contained"
        color="primary"
        onClick={handleSubmit(onSubmit)}
      >
        SUBMIT
      </Button>

      <div style={{ padding: "10px" }}>
        <FormProvider {...methods}>
          <form>
            <Grid container spacing={2}>
              <Grid item xs={6}>
                <FormInput name="name" label="Name" />
              </Grid>
              <Grid item xs={6}>
                <FormSelect name="sel" label="Numbers" options={numberData} />
              </Grid>
            </Grid>
          </form>
        </FormProvider>
      </div>
    </div>
  );
}

export default App;
```
What has changed in the *App.js*
* I have created data (array of objects) that we will pass to the `FormSelect`. 
```js
  const numberData = [
    {
      id: "10",
      label: "Ten",
    },
    {
      id: "20",
      label: "Twenty",
    },
    {
      id: "30",
      label: "Thirty",
    },
  ];
```
* I have added following code to the render
```js
<Grid item xs={6}>
    <FormSelect name="sel" label="Numbers" options={noData} />
</Grid>
```
Now your web page will look like:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/967gbnfbuqy83dka9cad.PNG)

Fill the form data, and click on *SUBMIT* button. Check the output in the dev-console.

## 3. Multi-Select with Autocomplete (React-Select) <a name="sub3"></a>

Here we will be using one of the most popular react libraries [React-Select](https://react-select.com/home). Create a new folder name *select-autocomplete* under *controls*, inside *select-autocomplete* folder create two files *index.js* and *index.css* file ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/qg61g8skajft5wffa9l7.PNG)

Now go to *index.js* under *styles* folder and add below code:

```js
export const StyledFormControl = styled(FormControl)`
  && {
    width: 100%;
    display: block;
    position: relative;
  }
`;

export const StyledAutoSelectInputLabel = styled(InputLabel)`
  && {
    position: relative;
    .req-label {
      color: #f44336;
    }
    transform: translate(0, 1.5px) scale(0.75);
    transform-origin: top left;
  }
`;
```
Now go to *index.css* under *select-autocomplete* folder and add below code:
```
.autoselect-options {
  padding: 6px 16px;
  line-height: 1.5;
  width: auto;
  min-height: auto;
  font-size: 1rem;
  letter-spacing: 0.00938em;
  font-weight: 400;
  cursor: pointer;
}

.autoselect-options:hover {
  background-color: rgba(0, 0, 0, 0.14) !important;
}

```
I have done the styling changes for two purposes, firstly it will be used when we add validation for error handling and second to make the React-Select look and feel close to Material UI Select. 

Now go to *index.js* under *select-autocomplete* folder and add below code:
```js
import React, { useEffect, useState } from "react";
import { useFormContext, Controller } from "react-hook-form";
import Select, { createFilter } from "react-select";
import { StyledFormControl, StyledAutoSelectInputLabel } from "../../styles";
import "./index.css";

const stylesReactSelect = {
  clearIndicator: (provided, state) => ({
    ...provided,
    cursor: "pointer",
  }),
  indicatorSeparator: (provided, state) => ({
    ...provided,
    margin: 0,
  }),
  dropdownIndicator: (provided, state) => ({
    ...provided,
    cursor: "pointer",
  }),
  placeholder: (provided, state) => ({
    ...provided,
    fontFamily: "Roboto, Helvetica, Arial, sans-serif",
    color: state.selectProps.error ? "#f44336" : "rgba(0, 0, 0, 0.54)",
  }),
  control: (provided, state) => ({
    ...provided,
    borderRadius: 0,
    border: 0,
    borderBottom: state.selectProps.error
      ? "1px solid #f44336"
      : "1px solid rgba(0,0,0,0.87)",
    boxShadow: "none",
    ":hover": {
      borderColor: state.selectProps.error ? "1px solid #f44336" : "inherit",
      boxShadow: state.selectProps.error ? "1px solid #f44336" : "none",
    },
  }),
  valueContainer: (provided, state) => ({
    ...provided,
    paddingLeft: 0,
  }),
};

const components = {
  Option,
};

function Option(props) {
  const { onMouseMove, onMouseOver, ...newInnerProps } = props.innerProps;
  return (
    <div {...newInnerProps} className="autoselect-options">
      {props.children}
    </div>
  );
}

const ReactSelect = (props) => {
  const { label, options, name } = props;
  return (
    <React.Fragment>
      <StyledFormControl>
        <StyledAutoSelectInputLabel>
          <span>{label}</span>
        </StyledAutoSelectInputLabel>
        <Select
          options={options}
          placeholder="Please Select"
          valueKey="id"
          components={components}
          isClearable={true}
          styles={stylesReactSelect}
          isSearchable={true}
          filterOption={createFilter({ ignoreAccents: false })}
          {...props}
        />
      </StyledFormControl>
    </React.Fragment>
  );
};

function FormSelectAutoComplete(props) {
  const { control } = useFormContext();
  const { name, label, options } = props;

  const [newData, setNewData] = useState([]);

  useEffect(() => {
    const newOptions = options.map((data, index) => ({
      label: data.label,
      value: data.id,
    }));
    setNewData(newOptions);
  }, [options]);

  return (
    <React.Fragment>
      <Controller
        as={ReactSelect}
        name={name}
        control={control}
        label={label}
        {...props}
        options={newData}
      />
    </React.Fragment>
  );
}

export default FormSelectAutoComplete;
```
Let's break down the code.
```js
const stylesReactSelect = {
  clearIndicator: (provided, state) => ({
    ...provided,
    cursor: "pointer",
  }),
  indicatorSeparator: (provided, state) => ({
    ...provided,
    margin: 0,
  }),
  dropdownIndicator: (provided, state) => ({
    ...provided,
    cursor: "pointer",
  }),
  placeholder: (provided, state) => ({
    ...provided,
    fontFamily: "Roboto, Helvetica, Arial, sans-serif",
    color: state.selectProps.error ? "#f44336" : "rgba(0, 0, 0, 0.54)",
  }),
  control: (provided, state) => ({
    ...provided,
    borderRadius: 0,
    border: 0,
    borderBottom: state.selectProps.error
      ? "1px solid #f44336"
      : "1px solid rgba(0,0,0,0.87)",
    boxShadow: "none",
    ":hover": {
      borderColor: state.selectProps.error ? "1px solid #f44336" : "inherit",
      boxShadow: state.selectProps.error ? "1px solid #f44336" : "none",
    },
  }),
  valueContainer: (provided, state) => ({
    ...provided,
    paddingLeft: 0,
  }),
};
```
* The above code is just styling change. As I have mentioned before I have done this to make the look and feel similar to Material UI Select to maintain the design consistency. You can refer to the complete style guide of *react-select* in this [link](https://react-select.com/styles)

```js
const components = {
  Option,
};

function Option(props) {
  const { onMouseMove, onMouseOver, ...newInnerProps } = props.innerProps;
  return (
    <div {...newInnerProps} className="autoselect-options">
      {props.children}
    </div>
  );
}
```
* The above code improves performance if you have a large data(around 100+ data objects)

```js
const ReactSelect = (props) => {
  const { label, options, name } = props;
  return (
    <React.Fragment>
      <StyledFormControl>
        <StyledAutoSelectInputLabel>
          <span>{label}</span>
        </StyledAutoSelectInputLabel>
        <Select
          options={options}
          placeholder="Please Select"
          valueKey="id"
          components={components}
          isClearable={true}
          styles={stylesReactSelect}
          isSearchable={true}
          filterOption={createFilter({ ignoreAccents: false })}
          {...props}
        />
      </StyledFormControl>
    </React.Fragment>
  );
};
```
* This is the UI part with label and react-select component. Similar to `FormSelect`, there are three main props `name`, `label` and `options`. `options` is an array of objects that contains the data to be displayed in the *react-select*.

```js
function FormSelectAutoComplete(props) {
  const { control } = useFormContext();
  const { name, label, options } = props;

  const [newData, setNewData] = useState([]);

  useEffect(() => {
    const newOptions = options.map((data, index) => ({
      label: data.label,
      value: data.id,
    }));
    setNewData(newOptions);
  }, [options]);

  return (
    <React.Fragment>
      <Controller
        as={ReactSelect}
        name={name}
        control={control}
        label={label}
        {...props}
        options={newData}
      />
    </React.Fragment>
  );
}
```
* `FormSelectAutoComplete` is similar to `FormSelect` component where again we are using `useFormContext()` method, `Controller` component and `control` object. 
One thing to note here is that the array of data objects that are passed to the `Select` component of *react-select* should have `label` and `value` key in the object. In the below code, I have purposefully passed data that doesn't have this `label` and `value` in the object, (which can be the case in the real world scenario) to show you what changes you need to do in order to satisfy this requirement.
```js
useEffect(() => {
	const newOptions = options.map((data, index) => ({
	  label: data.label,
	  value: data.id,
	}));
	setNewData(newOptions);
  }, [options]);
```
You don't need to do this if your data object contains `label` and `value` as a key.

Let's see how we consume `FormSelectAutoComplete` in the *App.js*. Below is the new code in *App.js*
```js
import React from "react";
import { useForm, FormProvider } from "react-hook-form";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";

import FormInput from "./controls/input";
import FormSelect from "./controls/select";
import FormSelectAutoComplete from "./controls/select-autocomplete";

function App(props) {
  const methods = useForm();
  const { handleSubmit } = methods;

  const onSubmit = (data) => {
    console.log(data);
  };

  const numberData = [
    {
      id: "10",
      label: "Ten",
    },
    {
      id: "20",
      label: "Twenty",
    },
    {
      id: "30",
      label: "Thirty",
    },
  ];

  return (
    <div style={{ padding: "10px" }}>
      <Button
        variant="contained"
        color="primary"
        onClick={handleSubmit(onSubmit)}
      >
        SUBMIT
      </Button>

      <div style={{ padding: "10px" }}>
        <FormProvider {...methods}>
          <form>
            <Grid container spacing={2}>
              <Grid item xs={6}>
                <FormInput name="name" label="Name" />
              </Grid>
              <Grid item xs={6}>
                <FormSelect name="sel" label="Numbers" options={numberData} />
              </Grid>
              <Grid item xs={6}>
                <FormSelectAutoComplete
                  name="selAuto"
                  label="Auto Select Numbers"
                  options={numberData}
                  isMulti
                />
              </Grid>
            </Grid>
          </form>
        </FormProvider>
      </div>
    </div>
  );
}

export default App;
```
What has changed in `App.js` is the below piece of code
```js
<Grid item xs={6}>
	<FormSelectAutoComplete
	  name="selAuto"
	  label="Auto Select Numbers"
	  options={numberData}
	  isMulti
	/>
</Grid>
```
Here we are using the same `numberData` array of objects that we used in the `FormSelect` because *react-select* takes array of objects as data which we have passed in the `options` prop. `isMulti` prop is used if we want to show multiple selected values. 

Now your web page will look like:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mh9dw7n5rnpe3uiq4tqq.PNG)

Fill the form data, and click on *SUBMIT* button. Check the output in the dev-console.

# Validation with Yup <a name="heading3"></a>
If you have a form, the chance is that 99% of the time you will have some sort of the validation. *React Hook Forms* provide various ways to do the validation ([Basic Validaiton](https://react-hook-form.com/get-started/#Handleerrors) & [Schema Validation](https://react-hook-form.com/get-started/#SchemaValidation)).
We are going to use [Yup](https://github.com/jquense/yup) for our validations.

Let's modify our `App.js`
```js
import React from "react";
import { useForm, FormProvider } from "react-hook-form";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";

import FormInput from "./controls/input";
import FormSelect from "./controls/select";
import FormSelectAutoComplete from "./controls/select-autocomplete";

import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers";

const validationSchema = yup.object().shape({
  nameV: yup.string().required("Name Validation Field is Required"),
  selV: yup.string().required("Select Validation Field is Required"),
  selAutoV: yup.array().required("Multi Select Validation Field required"),
});

function App(props) {
  const methods = useForm({
    resolver: yupResolver(validationSchema),
  });
  const { handleSubmit, errors } = methods;

  const onSubmit = (data) => {
    console.log(data);
  };

  const numberData = [
    {
      id: "10",
      label: "Ten",
    },
    {
      id: "20",
      label: "Twenty",
    },
    {
      id: "30",
      label: "Thirty",
    },
  ];

  return (
    <div style={{ padding: "10px" }}>
      <Button
        variant="contained"
        color="primary"
        onClick={handleSubmit(onSubmit)}
      >
        SUBMIT
      </Button>

      <div style={{ padding: "10px" }}>
        <FormProvider {...methods}>
          <form>
            <Grid container spacing={2}>
              <Grid item xs={6}>
                <FormInput name="name" label="Name" />
              </Grid>
              <Grid item xs={6}>
                <FormInput
                  name="nameV"
                  label="Name with Validation"
                  required={true}
                  errorobj={errors}
                />
              </Grid>
              <Grid item xs={6}>
                <FormSelect name="sel" label="Numbers" options={numberData} />
              </Grid>
              <Grid item xs={6}>
                <FormSelect
                  name="selV"
                  label="Numbers with Validation"
                  options={numberData}
                  required={true}
                  errorobj={errors}
                />
              </Grid>
              <Grid item xs={6}>
                <FormSelectAutoComplete
                  name="selAuto"
                  label="Auto Select Numbers"
                  options={numberData}
                  isMulti
                />
              </Grid>
              <Grid item xs={6}>
                <FormSelectAutoComplete
                  name="selAutoV"
                  label="Auto Select Numbers with Validation"
                  options={numberData}
                  isMulti
                  required={true}
                  errorobj={errors}
                />
              </Grid>
            </Grid>
          </form>
        </FormProvider>
      </div>
    </div>
  );
}

export default App;

```
Let's dissect the new code changes:

```js
import * as yup from "yup";
import { yupResolver } from "@hookform/resolvers";
```
* We have imported `yup` and `yupResolver`

```js
const validationSchema = yup.object().shape({
  nameV: yup.string().required("Name Validation Field is Required"),
  selV: yup.string().required("Select Validation Field is Required"),
  selAutoV: yup.array().required("Multi Select Validation Field required"),
});
```
* Create a `validationSchema` object as shown above. 
`nameV` is the name of the `FormInput` field to which the validation needs to be applied. The user input value will be of type "string" hence `yup.string()`. Since it is a required field  `yup.string().required()`. The custom error message can be passed to `required` function as shown above. 
Similarly, `selV` is the name of `FormSelect` field, where the value selected from the dropdown will be of type "string" hence `yup.string().required()`. The custom error message can be passed to `required` function as shown above.
`selAutoV` is the name of `FormSelectAutoComplete` field, where the value selected will be in the form of array of objects. Hence `yup.array().required()`.  The custom error message can be passed to `required` function as shown above.

What if we don't pass a custom error message, it will not throw an error but it will display some other message(Try this out!)

```js
 const methods = useForm({
    resolver: yupResolver(validationSchema),
  });
 const { handleSubmit, errors } = methods;
```
* Pass the `validationSchema` object to the `yupResolver` function as shown above. Also we will be using `errors` object from `methods` object which will contain the field that has an error along with the error message.


* We have added three new components `FormInput`, `FormSelect` & `FormSelectAutoComplete` with two new props ` required={true}` and `errorobj={errors}`
```js
<Grid item xs={6}>
	<FormInput
	  name="nameV"
	  label="Name with Validation"
	  required={true}
	  errorobj={errors}
	/>
 </Grid>
```

```js
  <Grid item xs={6}>
	<FormSelect
	  name="selV"
	  label="Numbers with Validation"
	  options={numberData}
	  required={true}
	  errorobj={errors}
	/>
  </Grid>
```


```js
  <Grid item xs={6}>
	<FormSelectAutoComplete
	  name="selAutoV"
	  label="Auto Select Numbers with Validation"
	  options={numberData}
	  isMulti
	  required={true}
	  errorobj={errors}
	/>
  </Grid>
```
Now we need to modify our `FormInput`, `FormSelect` & `FormSelectAutoComplete` component to highlight validation error and show respective error messages.
`FormInput`
* Create an *index.css* file in the *input* folder of *controls* (*controls -> input -> index.css*). *index.css* will have following code:

```
.required-label span {
    color: #f44336;
  }
```

```js
import React from "react";
import { useFormContext, Controller } from "react-hook-form";
import TextField from "@material-ui/core/TextField";
import "./index.css";

function FormInput(props) {
  const { control } = useFormContext();
  const { name, label, required, errorobj } = props;
  let isError = false;
  let errorMessage = "";
  if (errorobj && errorobj.hasOwnProperty(name)) {
    isError = true;
    errorMessage = errorobj[name].message;
  }

  return (
    <Controller
      as={TextField}
      name={name}
      control={control}
      defaultValue=""
      label={label}
      fullWidth={true}
      InputLabelProps={{
        className: required ? "required-label" : "",
        required: required || false,
      }}
      error={isError}
      helperText={errorMessage}
      {...props}
    />
  );
}

export default FormInput;
```

We have made the following changes:
```js
const { name, label, required, errorobj } = props;
  let isError = false;
  let errorMessage = "";
  if (errorobj && errorobj.hasOwnProperty(name)) {
    isError = true;
    errorMessage = errorobj[name].message;
  }
```
The `required` and `errorobj` which were passed as props to the `FormInput` component in `App.js` are being used above. `errorObj` consist of *name* of the field and *error message* which we have passed in the validation schema. This object is created by *react hook forms*. The above piece of code will be similar across `FormSelect` & `FormSelectAutoComplete` form components which we have created.

Next change which we did was to the `Controller` component
```js
    <Controller
      as={TextField}
      name={name}
      control={control}
      defaultValue=""
      label={label}
      fullWidth={true}
      InputLabelProps={{
        className: required ? "required-label" : "",
        required: required || false,
      }}
      error={isError}
      helperText={errorMessage}
      {...props}
    />
```
We added the following new props to the `Controller` component. 
```js
InputLabelProps={{
	className: required ? "required-label" : "",
	required: required || false,
}}
error={isError}
helperText={errorMessage}
```
`InputLabelProps`, `error` and `helperText` props are specified by Material UI `TextField` to control the styling of `TextField` and how to show an error message.

Similar code changes will be done to `FormSelect` and `FormSelectAutoComplete` component.
`FormSelect`
```js
import React from "react";
import { useFormContext, Controller } from "react-hook-form";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import Select from "@material-ui/core/Select";
import { StyledInputLabel } from "../../styles";
import FormHelperText from "@material-ui/core/FormHelperText";

const MuiSelect = (props) => {
  const { label, name, options, required, errorobj } = props;
  let isError = false;
  let errorMessage = "";
  if (errorobj && errorobj.hasOwnProperty(name)) {
    isError = true;
    errorMessage = errorobj[name].message;
  }

  return (
    <FormControl fullWidth={true} error={isError}>
      <StyledInputLabel htmlFor={name}>
        {label} {required ? <span className="req-label">*</span> : null}
      </StyledInputLabel>
      <Select id={name} {...props}>
        <MenuItem value="">
          <em>None</em>
        </MenuItem>
        {options.map((item) => (
          <MenuItem key={item.id} value={item.id}>
            {item.label}
          </MenuItem>
        ))}
      </Select>
      <FormHelperText>{errorMessage}</FormHelperText>
    </FormControl>
  );
};

function FormSelect(props) {
  const { control } = useFormContext();
  const { name, label } = props;
  return (
    <React.Fragment>
      <Controller
        as={MuiSelect}
        control={control}
        name={name}
        label={label}
        defaultValue=""
        {...props}
      />
    </React.Fragment>
  );
}

export default FormSelect;
```
`FormSelectAutoComplete`
```js
import React, { useEffect, useState } from "react";
import { useFormContext, Controller } from "react-hook-form";
import Select, { createFilter } from "react-select";
import { StyledFormControl, StyledAutoSelectInputLabel } from "../../styles";
import FormHelperText from "@material-ui/core/FormHelperText";
import "./index.css";

const stylesReactSelect = {
  clearIndicator: (provided, state) => ({
    ...provided,
    cursor: "pointer",
  }),
  indicatorSeparator: (provided, state) => ({
    ...provided,
    margin: 0,
  }),
  dropdownIndicator: (provided, state) => ({
    ...provided,
    cursor: "pointer",
  }),
  placeholder: (provided, state) => ({
    ...provided,
    fontFamily: "Roboto, Helvetica, Arial, sans-serif",
    color: state.selectProps.error ? "#f44336" : "rgba(0, 0, 0, 0.54)",
  }),
  control: (provided, state) => ({
    ...provided,
    borderRadius: 0,
    border: 0,
    borderBottom: state.selectProps.error
      ? "1px solid #f44336"
      : "1px solid rgba(0,0,0,0.87)",
    boxShadow: "none",
    ":hover": {
      borderColor: state.selectProps.error ? "1px solid #f44336" : "inherit",
      boxShadow: state.selectProps.error ? "1px solid #f44336" : "none",
    },
  }),
  valueContainer: (provided, state) => ({
    ...provided,
    paddingLeft: 0,
  }),
};

const components = {
  Option,
};

function Option(props) {
  const { onMouseMove, onMouseOver, ...newInnerProps } = props.innerProps;
  return (
    <div {...newInnerProps} className="autoselect-options">
      {props.children}
    </div>
  );
}

const ReactSelect = (props) => {
  const { label, options, required, errorobj, name } = props;
  let isError = false;
  let errorMessage = "";
  if (errorobj && errorobj.hasOwnProperty(name)) {
    isError = true;
    errorMessage = errorobj[name].message;
  }
  return (
    <React.Fragment>
      <StyledFormControl>
        <StyledAutoSelectInputLabel>
          <span className={isError ? "req-label" : ""}>
            {label} {required ? <span className="req-label">*</span> : null}
          </span>
        </StyledAutoSelectInputLabel>
        <Select
          options={options}
          placeholder="Please Select"
          valueKey="id"
          components={components}
          isClearable={true}
          styles={stylesReactSelect}
          isSearchable={true}
          filterOption={createFilter({ ignoreAccents: false })}
          error={isError}
          {...props}
        />
        {isError && (
          <FormHelperText error={isError}>{errorMessage}</FormHelperText>
        )}
      </StyledFormControl>
    </React.Fragment>
  );
};

function FormSelectAutoComplete(props) {
  const { control } = useFormContext();
  const { name, label, options } = props;

  const [newData, setNewData] = useState([]);

  useEffect(() => {
    const newOptions = options.map((data, index) => ({
      label: data.label,
      value: data.id,
    }));
    setNewData(newOptions);
  }, [options]);

  return (
    <React.Fragment>
      <Controller
        as={ReactSelect}
        name={name}
        control={control}
        label={label}
        defaultValue={[]}
        {...props}
        options={newData}
      />
    </React.Fragment>
  );
}

export default FormSelectAutoComplete;
```

Save the code, run the app and click on *SUBMIT* button. Your webpage will look like
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lflllt4yxn2k54qi83ff.PNG)

# Pre populating form field data <a name="heading4"></a>

There is always a scenario, where the form fields need to be pre-populated with some data e.g An Edit case of a web form.
*React Hook Forms* provide us with a method `setValue` to do that.
```js
setValue("name", "Ammar");
```
* Here `setValue` is the function which accepts two parameters. `name` is the name of the field, "Ammar" is the value of the field to be set.
* `setValue` function comes from `method` object of `useForm` function.
```js
 const methods = useForm();
 const {setValue} = methods;
```

# Github repo <a name="heading5"></a>
I have created few more form components like **Date Picker**, **Radio Buttons** and **Checkbox** and have also shown validation for the date as well. Also, all of the code in this tutorial is present in the repo. You can use this repo as a reference or directly use the code in your project.
[Repo](https://github.com/ammartinwala52/hook-form-mui)

# References <a name="heading6"></a>
* [React Hook Form](https://react-hook-form.com/get-started/)
* [Material UI](https://material-ui.com/)
* [Yup](https://github.com/jquense/yup)
* [React Select](https://react-select.com/home)
* [Material UI Pickers](https://material-ui-pickers.dev/getting-started/installation)
* [Styled Components](https://styled-components.com/)

##### If you have any queries/suggestion or found some issue with what I have explained in this article please let me know in the comments below. Thank you for your time in reading my article.