top of page

How to implement React Hook Form [V:7 - UseForm, Controller, MUI, and Errors]



React Hook Form leverages the ref attribute in DOM for forms.

Usually, we use React states to control the form input. When the state changes for every input to the field, it causes React to re-render, and hence performance of the component is affected.

That's where RHF comes to the rescue, bypasses the state, accesses the DOM node directly using ref, and changes the input value without using the state.

Therefore no re-renders, and naturally input field becomes uncontrolled and efficient.


We mostly use RHF with the third-party component library. So we will use MUI here.


UseForm and Controller:

Though RHF embraces and beverages uncontrolled components, it is hard to avoid controlled components in third-party packages like MUI.So we wrap the component with Controller.

import { useForm, Controller } from "react-hook-form";

const Demo = () => {
const { control } = useForm();
return (
<>
 <Controller
    render={({ field }) => <TextField {...field} label='Name'/>}
    name="name"
    control={control}
    defaultValue = ''
  />
  <Controller
    render={({ field }) => <TextField {...field} label='Mobile'/>}
    name="mobile"
    control={control}
    defaultValue = ''
  />
</>
)
}

Note: Controller is a wrapper component for using third-party UI libraries like MUI!

So we need to understand the props of the Controller,

Controller Props:

How to connect to third-party (MUI) components - 'render.'
How to leverage uncontrolled component performance - 'field'.
What is the name of the input? - 'name.'
How to connect component to RHF? - 'control.'
What is the default value? - 'defaultValue'
If default value is not given --> it will be undefined(Not advisable)
  • The controller is like a wrapper that facilitates third-party UI and RHF collaboration.

As most third-party libraries are controlled components based, we need a Controller component.

Getting the values of form:

Destructure 'handleSubmit' from useForm.

const Demo = () => {
const { control , handleSubmit} = useForm();

const formData = (values) => {
console.log(values)
}

return (
<>
<form onSubmit={handleSubmit(formData)}>
 <Controller
    render={({ field }) => <TextField {...field} label='Name'/>}
    name="name"
    control={control}
    defaultValue = ''
  />
  <Controller
    render={({ field }) => <TextField {...field} label='Mobile'/>}
    name="mobile"
    control={control}
    defaultValue = ''
  />
<Button type='submit'>Submit</Button>
</form>
</>
)
}

When you click submit button, you will get key-value pairs.

Key = name we give to the input, and the value will be what they have entered.

{name: "John", mobile: "2345678"}
Capturing errors:

Add 'rules' prop to Controller and take errors from formState

const Demo = () => {
const { control , handleSubmit ,formState:{errors} } = useForm();

const formData = (values) => {
console.log(values)
}

return (
<>
{
errors.name || errors.mobile ?
<p>Please fill mandatory fields</p>
:
undefined
}
<form onSubmit={handleSubmit(formData)}>
 <Controller
    render={({ field }) => <TextField {...field} label='Name'/>}
    name="name"
    control={control}
    defaultValue = ''
    rules={{required:true}}
  />
  <Controller
    render={({ field }) => <TextField {...field} label='Mobile'/>}
    name="mobile"
    control={control}
    defaultValue = ''
    rules={{required:true}}
  />
<Button type='submit'>Submit</Button>
</form>
</>
)
}

So "formState" has "errors" property. This "errors" property will be an object with type and message keys.

Type can be required, min, max, minLength, maxLength, pattern, validate


bottom of page