Use react-hook-form
Basic
Install
yarn add react-hook-form
Usage
- Create form elements with unique name props
...
return (
<form>
<input name="name" />
<input type="email" name="email" />
<input type="password" name="password" />
<button>Submit</button>
</form>
...
- Add handleSubmit method to manage form submission
import { useForm } from "react-hook-form";
...
const { register, handleSubmit } = useForm();
const onFormSubmit = data => console.log(data);
const onErrors = errors => console.error(errors);
return (
<form onSubmit={handleSubmit(onFormSubmit, onErrors)}>
...
</form>
);
- Register input fields into React Hook Form by using name values
import { useForm } from "react-hook-form";
...
const { register, handleSubmit } = useForm();
...
return (
<form onSubmit={handleSubmit(onFormSubmit, onErrors)}>
<input name="name" {...register('name')} />
<input type="email" name="email" {...register('email')} />
<input type="password" name="password" {...register('password')} />
<button>Submit</button>
</form>
);
- Create form validation object
...
const registerOptions = {
name: { required: "Name is required" },
email: { required: "Email is required" },
password: {
required: "Password is required",
minLength: {
value: 8,
message: "Password must have at least 8 characters"
}
}
};
...
- Apply validation for form element
...
<form onSubmit={handleSubmit(handleRegistration, handleError)}>
<input name="name" type="text" {...register('name', registerOptions.name) }/>
{errors?.name && errors.name.message}
<input
type="email"
name="email"
{...register('email', registerOptions.email)}
/>
{errors?.email && errors.email.message}
<input
type="password"
name="password"
{...register('password', registerOptions.password)}
/>
{errors?.password && errors.password.message}
<button>Submit</button>
</form>
...
- Or catch error state like this
const errorNameMessage = errors['name']?.message;
const hasNameError = !!(errors && errorNameMessage);
return <form>
<input name="name" type="text" {...register('name', registerOptions.name) }/>
{hasNameError && errorNameMessage}
...
How to make a submit button inside a form?
<button type='submit'>Click me</button>
Typescript template
How to create main component?
import InputForm from 'src/components/InputForm'
import { useForm, SubmitHandler } from 'react-hook-form'
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
type TFormFields = {
'name-first-name': string,
}
const registerRules = {
firstName: { required: 'First name is required' },
gender: { required: 'Gender is required' },
}
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
const Details = () => {
const {
control,
register,
handleSubmit,
formState: { errors },
} = useForm<TFormFields>()
const onSubmit: SubmitHandler<TFormFields> = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<InputForm
name='name-first-name'
register={register}
rules={registerRules.firstName}
errors={errors}
/>
<SelectBox
placeHolder='Gender'
name='name-select-gender'
errors={errors}
control={control}
options={[
{ value: 'male', label: 'Male' },
{ value: 'female', label: 'Female' },
]}
rules={registerRules.gender}
/>
</form>
)
}
export default Details
How to create a custom Input text?
import { ReactElement } from 'react'
import {
DeepMap,
FieldError,
FieldValues,
Path,
RegisterOptions,
UseFormRegister,
} from 'react-hook-form'
require('./index.scss')
type TInputProps<TSomething> = {
placeHolder: string,
name?: Path<TSomething>,
rules?: RegisterOptions,
register?: UseFormRegister<TSomething>,
errors?: Partial<DeepMap<FieldValues, FieldError>>
}
const InputForm = <TSomething extends unknown>({
name,
rules = {},
register,
errors = {},
placeHolder
}: TInputProps<TSomething>): ReactElement => {
const errorMessage = errors[name]?.message
const hasError = !!(errors && errorMessage)
return (
<div className={hasError ? `preferences__profile--input-box--error` : `preferences__profile--input-box`}>
<input
placeholder={placeHolder}
name={name || ''}
{...(register && register(name, rules))}
/>
{hasError && <p>{errorMessage}</p>}
</div>
)
}
export default InputForm
How to create a custom Select box with react-select?
import { ReactElement } from 'react'
import Select, { components } from 'react-select'
import { Control, Controller, DeepMap, FieldError, FieldValues, Path, RegisterOptions } from 'react-hook-form'
require('./index.scss')
interface Props<TSomething> {
placeHolder: string,
options: Array<any>,
name?: Path<TSomething>,
rules?: RegisterOptions,
control?: Control<TSomething, any>,
errors?: Partial<DeepMap<FieldValues, FieldError>>,
}
let hasError: boolean = false
const SelectBox = <TSomething extends unknown>({
placeHolder,
options,
rules,
control,
errors,
name,
}: Props<TSomething>): ReactElement => {
const errorMessage = errors?.[name]?.message
hasError = !!(errors && errorMessage)
return <>
<Controller
control={control}
name={name}
rules={rules}
render={({ field: { onChange, value, name, ref } }) => {
const currentSelection = options.find(
(c) => c.value === value
)
const handleSelectChange = (selectedOption: { value: string }) => {
onChange(selectedOption?.value);
}
return <div>
<Select
className={'preferences__select-box'}
components={{
DropdownIndicator: CustomDropdownIndicator,
IndicatorSeparator: () => null,
}}
styles={customStyles}
options={options}
placeholder={placeHolder}
onChange={handleSelectChange}
value={currentSelection}
/>
{hasError && <p className='preferences__profile--error-text'>{errorMessage}</p>}
</div>
}}
/>
</>
}
const customStyles = {
control: (provided: any) => ({
...provided,
width: '100%',
height: '48px',
paddingLeft: '7px',
border: hasError ? '1px solid #ED1C24' : '1px solid #DEDEDE',
fontSize: '14px',
color: '#333333',
}),
placeholder: (defaultStyles: any) => {
return {
...defaultStyles,
color: '#999999',
}
},
option: (provided: any, state: { isSelected: any }) => ({
...provided,
color: '#333333',
}),
}
const CustomDropdownIndicator = (props: any) => {
return (
<components.DropdownIndicator {...props}>
<YourOwnIcon />
</components.DropdownIndicator>
)
}
export default SelectBox
Leave a Reply