#React #ReactHookForm #Material-UI #React-Hooks #JavaScript
This article describes in a sneak peek what is the library react-hook-form and how to use it. First of all, you need to set up the environment by creating a new react application with the command create-react-app. If you don't know how to do it, please refer to my post Setting up the react environment with the super tool create-react-app before continuing in this tutorial.
We need to install the npm modules necessary to build this project which means it will be saved as a dependency in our package.json
file.
We are going to install material-ui and react-hook-form library locally.
To install the react-hook-form
module we have to type the following command on the terminal located in the project directory:
yarn add react-hook-form@6.5.3
I've installed the version 6.5.3.
To install the material-ui
module to give some styling to our example form, we have to type the following command:
yarn add @material-ui/core@4.11.0
yarn add @material-ui/icons@4.9.1
This commands are for people working on a MAC or Linux Machine, If you are on a windows you can install an emulator to type the commands as you were in a Linux Machine. Click here to download Hyper
mkdir components
touch components/SignInForm.js
touch styles.js
In the App.js copy the following code or import and add the SignInForm component:
import React from 'react';
import SignInForm from './components/SignInForm';
const App = () => (
<div>
<SignInForm />
</div>
);
export default App;
You can start the application as you see in the tutorial in the introduction of this article with the following command:
yarn run start
In this file, we are gonna change some classes, colours and styles of the final design of our Sign In Form based on the material-ui
library.
import TextField from '@material-ui/core/TextField';
import { makeStyles, withStyles } from '@material-ui/core/styles';
const mingColor = '#387780';
const dartmouthGreenColor = '#2D7638';
const emeraldGreenColor = '#62C370';
export const CssTextField = withStyles({
root: {
'& label.Mui-focused': {
color: mingColor,
},
'& .MuiInput-underline:after': {
borderBottomColor: dartmouthGreenColor,
},
'&$checked': {
color: '#3D70B2',
},
'& .MuiOutlinedInput-root': {
'& fieldset': {
borderColor: dartmouthGreenColor,
},
'&:hover fieldset': {
borderColor: emeraldGreenColor,
},
'&.Mui-focused fieldset': {
borderColor: mingColor,
},
},
},
})(TextField);
export const useStyles = makeStyles(theme => {
return {
paper: {
margin: theme.spacing(4, 0),
display: 'flex',
color: '#387780',
flexDirection: 'column',
alignItems: 'center',
border: `1px solid ${emeraldGreenColor}`,
borderRadius: '2rem',
padding: '1.5rem 2.5rem',
},
avatar: {
margin: theme.spacing(3),
backgroundColor: emeraldGreenColor,
fontSize: 50,
},
form: {
marginTop: theme.spacing(4),
width: '100%',
},
submit: {
margin: theme.spacing(3, 0, 2),
backgroundColor: emeraldGreenColor,
color: 'white',
padding: '50 50',
},
link: {
color: mingColor,
textDecoration: 'none !important',
},
checkBox: {
color: `${emeraldGreenColor} !important`,
},
error: {
color: 'red',
},
};
});
This is the component with the styles defined in the previous file applied and using the material-ui
library for final design.
import React from 'react';
import { AccountCircle as AccountCircleIcon } from '@material-ui/icons';
import {
Avatar,
Grid,
Container,
CssBaseline,
FormControlLabel,
Button,
Link,
Checkbox,
Typography,
} from '@material-ui/core';
import { CssTextField, useStyles } from '../styles';
const SignInForm = () => {
const classes = useStyles();
const onSubmit = e => {
e.preventDefault();
console.log(e.target);
};
return (
<Container component='main' maxWidth='xs'>
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<AccountCircleIcon style={{ fontSize: 45 }} />
</Avatar>
<Typography component='h1' variant='h4'>
Sign in
</Typography>
<form className={classes.form} noValidate onSubmit={e => onSubmit(e)}>
<CssTextField
name='email'
label='Email Address'
variant='outlined'
margin='normal'
autoComplete='email'
className={classes.margin}
fullWidth
required
autoFocus
/>
<CssTextField
name='password'
label='Password'
type='password'
variant='outlined'
margin='normal'
fullWidth
required
autoComplete='current-password'
/>
<Grid container>
<Grid item xs>
<Link href='#' variant='body2' className={classes.link}>
Forgot password?
</Link>
</Grid>
<Grid item>
<FormControlLabel
label='Remember me'
control={
<Checkbox
className={classes.checkBox}
name='remember'
defaultValue={false}
/>
}
/>
</Grid>
</Grid>
<Button
type='submit'
fullWidth
variant='contained'
className={classes.submit}
>
Sign In
</Button>
<Grid container>
<Grid item>
<Link href='#' variant='body2' className={classes.link}>
{'New to this platform? Create an Acount.'}
</Link>
</Grid>
</Grid>
</form>
</div>
</Container>
);
};
export default SignInForm;
Here we are going to explain some tweaks to be able to validate the data in the SignInForm
component.
First, we need to import useForm
hook and the Controller
component from the library:
import { useForm, Controller } from 'react-hook-form';
After, initializing the styles in the SignInForm
component we are going to add the register
and handleSubmit
functions, and errors
and control
objects:
const { register, handleSubmit, errors, control } = useForm();
However, If you want to configure the useForm
hook more, you can add an object with the details that you want to specify:
// ...
const { register, handleSubmit, control, errors } = useForm({
mode: 'onChange',
reValidateMode: 'onChange',
defaultValues: {
email: '',
password: '',
remember: false,
},
});
// ...
We are gonna update our onSubmit
function and integrate it with handleSubmit
hook from useFrom
inside the form tag:
// ...
const onSubmit = data => alert(JSON.stringify(data));
// ...
<form
className={classes.form}
noValidate
onSubmit={handleSubmit(onSubmit)}
>
// ...
In order to add each input field to the form data, we just need to reference the register
function in each component. For example, in an input
, we add the property ref. However, we are using material-ui
, so, to do the reference to the register function we use the property inputRef
instead. See the example below:
// ...
<CssTextField
name='email'
label='Email Address'
variant='outlined'
margin='normal'
autoComplete='email'
className={classes.margin}
fullWidth
required
autoFocus
inputRef={register}
/>
// ...
We can add an object to the register function to be able to set up different functionalities. For instance, in the email, we want the email to be required
and personalize the message
that the user will see if the email field is blank. Additionally, we want the email to follow a certain pattern
that every email has, so we are going to use a regex expression to do this and set up a message
if the email doesn´t follow the pattern established by this expression. We add an error property to the CssTextField
that this changes the colour to red if there is an error message to show about this field.
// ...
<CssTextField
name='email'
label='Email Address'
variant='outlined'
margin='normal'
inputRef={register({
required: 'You must provide the email address!',
pattern: {
value: /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
message: 'You must provide a valid email address!',
},
})}
autoComplete='email'
error={!!errors.email}
className={classes.margin}
fullWidth
autoFocus
/>
// ...
Now, we want that if occurs any of the errors aforementioned (required field and pattern) to show the message to the user. We use this code after the previous component:
{
errors.email && <span className={classes.error}>{errors.email.message}</span>;
}
// ...
In the password field we are going to set up as a required field and as the minimum length of 6 characters. The component with the register function and the error message display section will be like this:
// ...
<CssTextField
name='password'
label='Password'
type='password'
variant='outlined'
margin='normal'
inputRef={register({
required: 'You must provide a password.',
minLength: {
value: 6,
message: 'Your password must be greater than 6 characters',
},
})}
fullWidth
autoComplete='current-password'
/>;
{
errors.password && (
<span className={classes.error}>{errors.password.message}</span>
);
}
// ...
For Checkbox
component, there are two options. However, I prefer the second one which the defaultValue updates the initial state of the checkbox. This are the options:
This option is using the register function like in the previous components.
// ...
<FormControlLabel
label='Remember me'
name='remember'
control={<Checkbox className={classes.checkBox} inputRef={register()} />}
/>
// ...
In this option, we need to change the Checkbox to Controller inside the control property to be able to expose to register this field in the form data. In the Controller
component we add the control={control}
we put a defaultValue
right in line, and add the render property to set up the onChange event and checked value.
After the previous grid container, we add another one to add the checkTest
checkbox. You can see the component added into the code below:
// ...
</Grid>
<Grid container>
<FormControlLabel
control={
<Controller
control={control}
name='checkTest'
defaultValue={true}
render={({ onChange, value }) => (
<Checkbox
className={classes.checkBox}
onChange={e => onChange(e.target.checked)}
checked={value}
/>
)}
/>
}
label='Checkbox with Controller and render'
/>
</Grid>
<Button
// ...
Now that we have all in place let's test it out.
- To install the devtools in the dev dependencies package in the project executing the following command:
yarn add @hookform/devtools@2.2.1 -D
The latest version at this time is 2.2.1
.
- import the
DevTool
in theSignInForm
Component:
import { DevTool } from '@hookform/devtools';
- We have already the control object in the useForm declaration:
const { register, handleSubmit, watch, control, errors } = useForm();
- After the Container component we add the DevTool:
// ...
return (
<Container component='main' maxWidth='xs'>
<DevTool control={control} />
// ...
That's it. You will see the dev tools on the browser as soon as it renders again after you save the file.