Это краткий пример того, как настроить проверку формы в Next.js с библиотекой форм React Hook.
Первоначально опубликовано здесь https://tkssharma.com/nextjs-with-react-hook-forms-building-forms/
React Hook Form — это библиотека для работы с формами в React с использованием React Hooks, я наткнулся на нее около года назад и с тех пор использую в своих проектах Next.js и React, думаю, ее проще использовать, чем другие варианты доступен и требует меньше кода. Для получения дополнительной информации см. https://react-hook-form.com.
Установка Для установки React Hook Form требуется всего одна команда, и вы готовы к работе.
npm install react-hook-form
Пример Следующий фрагмент кода демонстрирует базовый пример использования:
import React from 'react'; import { useForm } from 'react-hook-form';
export default function App() { const { register, handleSubmit, watch, formState: { errors }, } = useForm(); const onSubmit = (data) => console.log(data);
console.log(watch('example')); // watch input value by passing the name of it
return ( /* "handleSubmit" will validate your inputs before invoking "onSubmit" */ <form onSubmit={handleSubmit(onSubmit)}> {/* register your input into the hook by invoking the "register" function */} <input defaultValue="test" {...register('example')} />
{/* include validation with required or other standard HTML validation rules */} <input {...register('exampleRequired', { required: true })} /> {/* errors will return when field validation fails */} {errors.exampleRequired && <span>This field is required</span>}
<input type="submit" /> </form> ); }
Примером является простая регистрационная форма с довольно стандартными полями для титула, имени, фамилии, даты рождения, электронной почты, пароля, подтверждения пароля и флажка «Принять условия». Все поля обязательны, включая флажок, дата рождения должна быть действительной датой, адрес электронной почты должен быть в допустимом формате, поле пароля должно иметь минимальную длину 6, а поля подтверждения пароля и пароля должны совпадать.
Вот он в действии: (см. CodeSandbox на https://codesandbox.io/s/next-js-form-validation-example-forked-1e2soi)
Домашняя страница Next.js с формой React Hook На домашней странице находится пример регистрационной формы, созданной с помощью библиотеки форм React Hook.
Правила проверки формы определяются с помощью библиотеки проверки схемы Yup и передаются функции использования формы React Hook Form, для получения дополнительной информации о Yup см. https://github.com/jquense/yup.
Правила проверки формы определяются с помощью библиотеки проверки схемы Yup и передаются с помощью formOptions в функцию React Hook Form useForm(), для получения дополнительной информации о Yup см. https://github.com/jquense/yup.
Хук-функция useForm() возвращает объект с методами для работы с формой, включая регистрацию ввода, обработку отправки формы, сброс формы, доступ к состоянию формы, отображение ошибок и многое другое, полный список см. https://react-hook -form.com/api/useform.
Метод onSubmit() вызывается, когда форма действительна и отправлена, и просто отображает данные формы в предупреждении javascript.
Возвращенный шаблон JSX содержит форму со всеми полями ввода и сообщениями проверки. Поля формы регистрируются в форме React Hook путем вызова функции регистрации с именем поля из каждого элемента ввода (например, {…register(‘title’)}).
Чтобы установить React Hook Form, выполните следующую команду:
npm install react-hook-form
Как использовать React Hooks в форме В этом разделе вы узнаете об основах useForm Hook, создав очень простую регистрационную форму.
Сначала импортируйте хук useForm из пакета react-hook-form:
import { useForm } from "react-hook-form"; // Then, inside your component, use the Hook as follows:
const { register, handleSubmit } = useForm();
Хук useForm возвращает объект, содержащий несколько свойств. На данный момент вам требуется только регистрация и handleSubmit.
Метод register помогает зарегистрировать поле ввода в форме React Hook, чтобы оно было доступно для проверки, а его значение можно было отслеживать на предмет изменений.
Чтобы зарегистрировать ввод, мы передадим метод register в поле ввода как таковой:
<input type="text" name="firstName" {...register('firstName')} />
Этот синтаксис оператора расширения является новой реализацией библиотеки, которая обеспечивает строгую проверку типов в формах с помощью TypeScript. Вы можете узнать больше о строгой проверке типов в React Hook Form здесь.
В версиях старше v7 метод register был прикреплен к атрибуту ref как таковой:
<input type="text" name="firstName" ref={register} />
Обратите внимание, что входной компонент должен иметь имя, а его значение должно быть уникальным.
Метод handleSubmit, как следует из названия, управляет отправкой формы. Его нужно передать как значение свойства onSubmit компонента формы.
Метод handleSubmit может обрабатывать две функции в качестве аргументов. Первая функция, переданная в качестве аргумента, будет вызвана вместе с зарегистрированными значениями полей, когда проверка формы будет успешной. Вторая функция вызывается с ошибками при сбое проверки.
const onFormSubmit = data => console.log(data);
const onErrors = errors => console.error(errors);
<form onSubmit={handleSubmit(onFormSubmit, onErrors)}>
{/* ... */}
</form>
Теперь, когда у вас есть четкое представление об основном использовании хука useForm, давайте взглянем на более реалистичный пример:
import React from "react"; import { useForm } from "react-hook-form";
const RegisterForm = () => { const { register, handleSubmit } = useForm(); const handleRegistration = (data) => console.log(data);
return ( <form onSubmit={handleSubmit(handleRegistration)}> <div> <label>Name</label> <input name="name" {...register('name')} /> </div> <div> <label>Email</label> <input type="email" name="email" {...register('email')} /> </div> <div> <label>Password</label> <input type="password" name="password" {...register('password')} /> </div> <button>Submit</button> </form> ); }; export default RegisterForm;
Как видите, никакие другие компоненты для отслеживания входных значений не импортировались. Хук useForm делает код компонента чище и проще в обслуживании, а поскольку форма не контролируется, вам не нужно передавать реквизиты, такие как onChange и value, для каждого ввода.
Вы можете использовать любую другую библиотеку пользовательского интерфейса по вашему выбору для создания формы. Но сначала обязательно проверьте документы и найдите реквизит, используемый для доступа к ссылочному атрибуту нативного компонента ввода.
В следующем разделе вы узнаете, как обрабатывать проверку формы в форме, которую мы только что создали.
Как проверять формы с помощью React Hook Form
Чтобы применить проверки к полю, вы можете передать параметры проверки в метод регистрации. Параметры проверки аналогичны существующему стандарту проверки формы HTML.
Эти параметры проверки включают следующие свойства:
required указывает, является ли поле обязательным или нет. Если для этого свойства установлено значение true, то поле не может быть пустым. minlength и maxlength устанавливают минимальную и максимальную длину для строкового входного значения. min и max устанавливают минимальное и максимальное значения для числового значения. это может быть электронная почта, число, текст или любые другие стандартные типы ввода HTML.
<input name="name" type="text" {...register('name', { required: true } )} />
Теперь попробуйте отправить форму с пустым полем. Это приведет к следующему объекту ошибки:
{
name: {
type: "required",
message: "",
ref: <input name="name" type="text" />
}
}
Здесь свойство type относится к типу неудачной проверки, а свойство ref содержит собственный элемент ввода DOM.
Вы также можете включить пользовательское сообщение об ошибке для поля, передав строку вместо логического значения свойству проверки:
<form onSubmit={handleSubmit(handleRegistration, handleError)}>
<div>
<label>Name</label>
<input name="name" {...register('name', { required: "Name is required" } )} />
</div>
</form>
Затем получите доступ к объекту ошибок с помощью хука useForm:
const { register, handleSubmit, formState: { errors } } = useForm(); You can display errors to your users like so: // {errors?.name && errors.name.message}
const RegisterForm = () => { const { register, handleSubmit, formState: { errors } } = useForm(); const handleRegistration = (data) => console.log(data);
return ( <form onSubmit={handleSubmit(handleRegistration)}> <div> <label>Name</label> <input type="text" name="name" {...register('name')} /> {errors?.name && errors.name.message} </div> {/* more input fields... */} <button>Submit</button> </form> ); };
Ниже вы можете найти полный пример:
import React from "react"; import { useForm } from "react-hook-form";
const RegisterForm = () => { const { register, handleSubmit, formState: { errors } } = useForm(); const handleRegistration = (data) => console.log(data); const handleError = (errors) => {};
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" } } };
return ( <form onSubmit={handleSubmit(handleRegistration, handleError)}> <div> <label>Name</label> <input name="name" type="text" {...register('name', registerOptions.name) }/> <small className="text-danger"> {errors?.name && errors.name.message} </small> </div> <div> <label>Email</label> <input type="email" name="email" {...register('email', registerOptions.email)} /> <small className="text-danger"> {errors?.email && errors.email.message} </small> </div> <div> <label>Password</label> <input type="password" name="password" {...register('password', registerOptions.password)} /> <small className="text-danger"> {errors?.password && errors.password.message} </small> </div> <button>Submit</button> </form> ); }; export default RegisterForm;
Если вы хотите проверить поле при возникновении события onChange или onBlur, вы можете передать свойство режима в хук useForm:
const { register, handleSubmit, errors } = useForm({
mode: "onBlur"
});
React Hook Form предоставляет компонент контроллера-оболочки, который позволяет вам регистрировать контролируемый внешний компонент, аналогично тому, как работает метод регистрации. В этом случае вместо метода register вы будете использовать объект управления из хука useForm:
const { register, handleSubmit, control } = useForm();
Учтите, что вам нужно создать в форме поле роли, которое будет принимать значения из выбранного ввода. Вы можете создать ввод выбора, используя библиотеку реагирования-выбора.
Объект управления должен быть передан в элемент управления компонента Controller вместе с именем поля. Вы можете указать правила проверки, используя свойство rules.
Управляемый компонент должен быть передан компоненту контроллера с помощью реквизита as. Компоненту Select также требуется свойство options для отображения раскрывающихся опций:
<Controller
name="role"
control={control}
defaultValue=""
rules={registerOptions.role}
render={({ field }) => (
<Select options={selectOptions} {...field} label="Text field" />
)}
/>
Приведенная выше поддержка рендеринга предоставляет onChange, onBlur, имя, ссылку и значение дочернему компоненту. Распространяя поле на компонент Select, React Hook Form регистрирует поле ввода.
Вы можете проверить полный пример для поля роли ниже:
import { useForm, Controller } from "react-hook-form"; import Select from "react-select"; // ... const { register, handleSubmit, errors, control } = useForm({ // use mode to specify the event that triggers each input field mode: "onBlur" });
const selectOptions = [ { value: "student", label: "Student" }, { value: "developer", label: "Developer" }, { value: "manager", label: "Manager" } ];
const registerOptions = { role: { required: "Role is required" } }; <form> <div> <label>Your Role</label> <Controller name="role" control={control} defaultValue="" rules={registerOptions.role} render={({ field }) => ( <Select options={selectOptions} {...field} label="Text field" /> )} /> <small className="text-danger"> {errors?.role && errors.role.message} </small> </div> </form>
Вы также можете просмотреть справку по API для компонента Controller здесь для подробного объяснения.
Давайте проверим еще несколько примеров с yup для проверки
import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as Yup from 'yup';
export default Home;
function Home() { // form validation rules const validationSchema = Yup.object().shape({ title: Yup.string().required('Title is required'), firstName: Yup.string().required('First Name is required'), lastName: Yup.string().required('Last name is required'), dob: Yup.string() .required('Date of Birth is required') .matches( /^\d{4}-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])$/, 'Date of Birth must be a valid date in the format YYYY-MM-DD' ), email: Yup.string() .required('Email is required') .email('Email is invalid'), password: Yup.string() .min(6, 'Password must be at least 6 characters') .required('Password is required'), confirmPassword: Yup.string() .oneOf([Yup.ref('password'), null], 'Passwords must match') .required('Confirm Password is required'), acceptTerms: Yup.bool().oneOf([true], 'Accept Ts & Cs is required'), }); const formOptions = { resolver: yupResolver(validationSchema) };
// get functions to build form with useForm() hook const { register, handleSubmit, reset, formState } = useForm(formOptions); const { errors } = formState;
function onSubmit(data) { // display form data on success alert('SUCCESS!! :-)\n\n' + JSON.stringify(data, null, 4)); return false; }
return ( <div className="card m-3"> <h5 className="card-header">Next.js - Form Validation Example</h5> <div className="card-body"> <form onSubmit={handleSubmit(onSubmit)}> <div className="form-row"> <div className="form-group col"> <label>Title</label> <select name="title" {...register('title')} className={`form-control ${errors.title ? 'is-invalid' : ''}`} > <option value=""></option> <option value="Mr">Mr</option> <option value="Mrs">Mrs</option> <option value="Miss">Miss</option> <option value="Ms">Ms</option> </select> <div className="invalid-feedback">{errors.title?.message}</div> </div> <div className="form-group col-5"> <label>First Name</label> <input name="firstName" type="text" {...register('firstName')} className={`form-control ${errors.firstName ? 'is-invalid' : ''}`} /> <div className="invalid-feedback">{errors.firstName?.message}</div> </div> <div className="form-group col-5"> <label>Last Name</label> <input name="lastName" type="text" {...register('lastName')} className={`form-control ${errors.lastName ? 'is-invalid' : ''}`} /> <div className="invalid-feedback">{errors.lastName?.message}</div> </div> </div> <div className="form-row"> <div className="form-group col"> <label>Date of Birth</label> <input name="dob" type="date" {...register('dob')} className={`form-control ${errors.dob ? 'is-invalid' : ''}`} /> <div className="invalid-feedback">{errors.dob?.message}</div> </div> <div className="form-group col"> <label>Email</label> <input name="email" type="text" {...register('email')} className={`form-control ${errors.email ? 'is-invalid' : ''}`} /> <div className="invalid-feedback">{errors.email?.message}</div> </div> </div> <div className="form-row"> <div className="form-group col"> <label>Password</label> <input name="password" type="password" {...register('password')} className={`form-control ${errors.password ? 'is-invalid' : ''}`} /> <div className="invalid-feedback">{errors.password?.message}</div> </div> <div className="form-group col"> <label>Confirm Password</label> <input name="confirmPassword" type="password" {...register('confirmPassword')} className={`form-control ${errors.confirmPassword ? 'is-invalid' : ''}`} /> <div className="invalid-feedback">{errors.confirmPassword?.message}</div> </div> </div> <div className="form-group form-check"> <input name="acceptTerms" type="checkbox" {...register('acceptTerms')} id="acceptTerms" className={`form-check-input ${errors.acceptTerms ? 'is-invalid' : ''}`} /> <label htmlFor="acceptTerms" className="form-check-label"> Accept Terms & Conditions </label> <div className="invalid-feedback">{errors.acceptTerms?.message}</div> </div> <div className="form-group"> <button type="submit" className="btn btn-primary mr-1"> Register </button> <button type="button" onClick={() => reset()} className="btn btn-secondary"> Reset </button> </div> </form> </div> </div> ); }
Другой пример
export function InputForm({ services, handleFormSubmit, servicesLocations, serviceType, userProfile }: inputProps) {
const validationSchema = Yup.object().shape({ orderBy: Yup.string().required('order by is required'), phoneNumber: Yup.string().required('phone is required'), orderDate: Yup.string().required('order date is required'), regarding: Yup.string().required('regarding is required'), }); const formOptions: any = { resolver: yupResolver(validationSchema), reValidateMode: 'onChange', mode: 'onChange' };
const { register, getValues, handleSubmit, formState } = useForm<any>(formOptions); const { errors } = formState;
const onSubmit = (e: any) => { const { orderDate, orderBy, instructions, extension, regarding, phoneNumber } = getValues(); // submit };
return ( <form onSubmit={handleSubmit(onSubmit)} className="px-4 py-5 sm:p-6"> <div className="grid grid-cols-6 gap-6 pb-8 border-b-2 border-pc-base-black"> <div className="col-span-6 sm:col-span-3"> <label className="block text-sm font-bold text-gray-800 font-source">Ordered By</label> <input defaultValue={userProfile.name} className={`w-full p-2 mt-2 border-2 rounded shadow-sm outline-none border-pc-base-black focus:border-gray-300 ${errors.orderBy ? 'is-invalid' : '' }`} {...register('orderBy')} type="text" name="orderBy" id="orderBy" readOnly autoComplete="orderBy" /> {errors.orderBy?.message && <FormError errorMessage={errors.orderBy?.message} />} </div>
<div className="col-span-6 sm:col-span-3"> <label className="block text-sm font-bold text-gray-700 font-source">Ordered date</label> <input {...register('orderDate')} defaultValue={getToday()} type="text" name="orderDate" readOnly id="orderDate" autoComplete="orderDate" className={`w-full p-2 mt-2 border-2 rounded shadow-sm outline-none border-pc-base-black focus:border-gray-300 ${errors.orderDate ? 'is-invalid' : '' }`} /> {errors.orderDate?.message && <FormError errorMessage={errors.orderDate?.message} />} </div>
<div className="col-span-6 sm:col-span-3"> <label className="block text-sm font-bold text-gray-700 font-source">Phone Number</label> <input defaultValue={userProfile.phoneNumber} {...register('phoneNumber')} type="text" name="phoneNumber" readOnly id="phoneNumber" autoComplete="phoneNumber" className={`w-full p-2 mt-2 border-2 rounded shadow-sm outline-none border-pc-base-black focus:border-gray-300 ${errors.phoneNumber ? 'is-invalid' : '' }`} /> {errors.phoneNumber?.message && <FormError errorMessage={errors.phoneNumber?.message} />} </div>
<div className="col-span-6 sm:col-span-3"> <label className="block text-sm font-bold text-gray-700 font-source">Extension</label> <input type="text" name="extension" id="extension" autoComplete="extension" className="w-full p-2 mt-2 border-2 rounded shadow-sm outline-none border-pc-base-black focus:border-gray-300" /> </div>
<div className="col-span-6 sm:col-span-3"> <label className="block text-sm font-bold text-gray-700 font-source">Medical Record Number</label> <input {...register('regarding')} type="text" name="regarding" id="regarding" placeholder='MRN #' autoComplete="regarding" className={`w-full p-2 mt-2 border-2 rounded shadow-sm outline-none border-pc-base-black focus:border-gray-300 ${errors.regarding ? 'is-invalid' : '' }`} /> {errors.regarding?.message && <FormError errorMessage={errors.regarding?.message} />} </div> <div className="col-span-6 sm:col-span-3"> <label className="block text-sm font-bold text-gray-700 font-source">Special Instructions</label> <input type="text" name="instructions" id="instructions" autoComplete="instructions" className="w-full p-2 mt-2 border-2 rounded shadow-sm outline-none border-pc-base-black focus:border-gray-300" /> </div> </div> </div> </form> )
Заключение
Я использую формы реагирования на хуки для всех своих проектов, и это работает как шарм, он предоставил все, что нам нужно, мы также можем посмотреть на библиотеку formik в реакции для проверки формы и обработки формы.