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